blob: baee6b233431687ea3c519e915eef089ac0a594b [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):
Mike Frysingerc177f942021-05-04 08:06:36 -0400173 if self.manifest:
174 try:
175 self.PARALLEL_JOBS = self.manifest.default.sync_j
176 except ManifestParseError:
177 pass
Mike Frysinger9180a072021-04-13 14:57:40 -0400178 super()._CommonOptions(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700179
Mike Frysinger9180a072021-04-13 14:57:40 -0400180 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400181 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
182 help='number of network jobs to run in parallel (defaults to --jobs)')
183 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
184 help='number of local checkout jobs to run in parallel (defaults to --jobs)')
185
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500186 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200187 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400188 help='obsolete option (to be deleted in the future)')
189 p.add_option('--fail-fast',
190 dest='fail_fast', action='store_true',
191 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700192 p.add_option('--force-sync',
193 dest='force_sync', action='store_true',
194 help="overwrite an existing git directory if it needs to "
195 "point to a different object directory. WARNING: this "
196 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500197 p.add_option('--force-remove-dirty',
198 dest='force_remove_dirty', action='store_true',
199 help="force remove projects with uncommitted modifications if "
200 "projects no longer exist in the manifest. "
201 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900202 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700203 dest='local_only', action='store_true',
204 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900205 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100206 dest='mp_update', action='store_false', default='true',
207 help='use the existing manifest checkout as-is. '
208 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900209 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700210 dest='network_only', action='store_true',
211 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900212 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700213 dest='detach_head', action='store_true',
214 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900215 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700216 dest='current_branch_only', action='store_true',
217 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400218 p.add_option('--no-current-branch',
219 dest='current_branch_only', action='store_false',
220 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500221 p.add_option('-m', '--manifest-name',
222 dest='manifest_name',
223 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700224 p.add_option('--clone-bundle', action='store_true',
225 help='enable use of /clone.bundle on HTTP/HTTPS')
226 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700227 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800228 p.add_option('-u', '--manifest-server-username', action='store',
229 dest='manifest_server_username',
230 help='username to authenticate with the manifest server')
231 p.add_option('-p', '--manifest-server-password', action='store',
232 dest='manifest_server_password',
233 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800234 p.add_option('--fetch-submodules',
235 dest='fetch_submodules', action='store_true',
236 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800237 p.add_option('--use-superproject', action='store_true',
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
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800273 def _GetBranch(self):
274 """Returns the branch name for getting the approved manifest."""
275 p = self.manifest.manifestProject
276 b = p.GetBranch(p.CurrentBranch)
277 branch = b.merge
278 if branch.startswith(R_HEADS):
279 branch = branch[len(R_HEADS):]
280 return branch
281
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700282 def _GetCurrentBranchOnly(self, opt):
283 """Returns True if current-branch or use-superproject options are enabled."""
Xin Li0cb6e922021-06-16 10:19:00 -0700284 return opt.current_branch_only or git_superproject.UseSuperproject(opt, self.manifest)
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700285
Raman Tenneti7954de12021-07-28 14:36:49 -0700286 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests, superproject_logging_data):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800287 """Update revisionId of every project with the SHA from superproject.
288
289 This function updates each project's revisionId with SHA from superproject.
290 It writes the updated manifest into a file and reloads the manifest from it.
291
292 Args:
293 opt: Program options returned from optparse. See _Options().
294 args: Arguments to pass to GetProjects. See the GetProjects
295 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700296 load_local_manifests: Whether to load local manifests.
Raman Tenneti7954de12021-07-28 14:36:49 -0700297 superproject_logging_data: A dictionary of superproject data that is to be logged.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800298
299 Returns:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700300 Returns path to the overriding manifest file instead of None.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800301 """
Raman Tennetib55769a2021-08-13 11:47:24 -0700302 print_messages = git_superproject.PrintMessages(opt, self.manifest)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800303 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800304 self.repodir,
Raman Tenneti784e16f2021-06-11 17:29:45 -0700305 self.git_event_log,
Raman Tennetib55769a2021-08-13 11:47:24 -0700306 quiet=opt.quiet,
307 print_messages=print_messages)
Raman Tennetiae86a462021-07-27 08:54:59 -0700308 if opt.local_only:
309 manifest_path = superproject.manifest_path
310 if manifest_path:
311 self._ReloadManifest(manifest_path, load_local_manifests)
312 return manifest_path
313
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800314 all_projects = self.GetProjects(args,
315 missing_ok=True,
316 submodules_ok=opt.fetch_submodules)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700317 update_result = superproject.UpdateProjectsRevisionId(all_projects)
318 manifest_path = update_result.manifest_path
Raman Tenneti7954de12021-07-28 14:36:49 -0700319 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700320 if manifest_path:
321 self._ReloadManifest(manifest_path, load_local_manifests)
322 else:
Raman Tennetib55769a2021-08-13 11:47:24 -0700323 if print_messages:
324 print('warning: Update of revisionId from superproject has failed, '
325 'repo sync will not use superproject to fetch the source. ',
326 'Please resync with the --no-use-superproject option to avoid this repo warning.',
327 file=sys.stderr)
Raman Tenneti8db30d62021-07-06 21:30:06 -0700328 if update_result.fatal and opt.use_superproject is not None:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700329 sys.exit(1)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800330 return manifest_path
331
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500332 def _FetchProjectList(self, opt, projects):
333 """Main function of the fetch worker.
334
335 The projects we're given share the same underlying git object store, so we
336 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800337
David James8d201162013-10-11 17:03:19 -0700338 Delegates most of the work to _FetchHelper.
339
340 Args:
341 opt: Program options returned from optparse. See _Options().
342 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700343 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500344 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700345
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500346 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700347 """Fetch git objects for a single project.
348
David Pursehousec1b86a22012-11-14 11:36:51 +0900349 Args:
350 opt: Program options returned from optparse. See _Options().
351 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700352
353 Returns:
354 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900355 """
David Rileye0684ad2017-04-05 00:02:59 -0700356 start = time.time()
357 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500358 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900359 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500360 success = project.Sync_NetworkHalf(
361 quiet=opt.quiet,
362 verbose=opt.verbose,
363 output_redir=buf,
364 current_branch_only=self._GetCurrentBranchOnly(opt),
365 force_sync=opt.force_sync,
366 clone_bundle=opt.clone_bundle,
367 tags=opt.tags, archive=self.manifest.IsArchive,
368 optimized_fetch=opt.optimized_fetch,
369 retry_fetches=opt.retry_fetches,
370 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400371 ssh_proxy=self.ssh_proxy,
Raman Tennetif32f2432021-04-12 20:57:25 -0700372 clone_filter=self.manifest.CloneFilter,
373 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700374
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500375 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400376 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500377 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700378
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500379 if not success:
380 print('error: Cannot fetch %s from %s'
381 % (project.name, project.remote.url),
382 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700383 except GitError as e:
384 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500385 except Exception as e:
386 print('error: Cannot fetch %s (%s: %s)'
387 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
388 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500389
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500390 finish = time.time()
391 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700392
Mike Frysinger339f2df2021-05-06 00:44:42 -0400393 @classmethod
394 def _FetchInitChild(cls, ssh_proxy):
395 cls.ssh_proxy = ssh_proxy
396
397 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500398 ret = True
399
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400400 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700401 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400402 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800403
David James89ece422014-01-09 18:51:58 -0800404 objdir_project_map = dict()
405 for project in projects:
406 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500407 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700408
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500409 def _ProcessResults(results_sets):
410 ret = True
411 for results in results_sets:
412 for (success, project, start, finish) in results:
413 self._fetch_times.Set(project, finish - start)
414 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
415 start, finish, success)
416 # Check for any errors before running any more tasks.
417 # ...we'll let existing jobs finish, though.
418 if not success:
419 ret = False
420 else:
421 fetched.add(project.gitdir)
422 pm.update(msg=project.name)
423 if not ret and opt.fail_fast:
424 break
425 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700426
Mike Frysinger339f2df2021-05-06 00:44:42 -0400427 # We pass the ssh proxy settings via the class. This allows multiprocessing
428 # to pickle it up when spawning children. We can't pass it as an argument
429 # to _FetchProjectList below as multiprocessing is unable to pickle those.
430 Sync.ssh_proxy = None
431
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500432 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400433 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400434 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500435 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
436 ret = False
437 else:
438 # Favor throughput over responsiveness when quiet. It seems that imap()
439 # will yield results in batches relative to chunksize, so even as the
440 # children finish a sync, we won't see the result until one child finishes
441 # ~chunksize jobs. When using a large --jobs with large chunksize, this
442 # can be jarring as there will be a large initial delay where repo looks
443 # like it isn't doing anything and sits at 0%, but then suddenly completes
444 # a lot of jobs all at once. Since this code is more network bound, we
445 # can accept a bit more CPU overhead with a smaller chunksize so that the
446 # user sees more immediate & continuous feedback.
447 if opt.quiet:
448 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800449 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500450 pm.update(inc=0, msg='warming up')
451 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800452 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
453 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500454 results = pool.imap_unordered(
455 functools.partial(self._FetchProjectList, opt),
456 projects_list,
457 chunksize=chunksize)
458 if not _ProcessResults(results):
459 ret = False
460 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800461
Mike Frysinger339f2df2021-05-06 00:44:42 -0400462 # Cleanup the reference now that we're done with it, and we're going to
463 # release any resources it points to. If we don't, later multiprocessing
464 # usage (e.g. checkouts) will try to pickle and then crash.
465 del Sync.ssh_proxy
466
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700467 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700468 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700469
Julien Campergue335f5ef2013-10-16 11:02:35 +0200470 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400471 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200472
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500473 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700474
Mike Frysingerb4429432021-05-05 20:03:26 -0400475 def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400476 load_local_manifests, ssh_proxy):
Mike Frysingerb4429432021-05-05 20:03:26 -0400477 """The main network fetch loop.
478
479 Args:
480 opt: Program options returned from optparse. See _Options().
481 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200482 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400483 err_event: Whether an error was hit while processing.
484 manifest_name: Manifest file to be reloaded.
485 load_local_manifests: Whether to load local manifests.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400486 ssh_proxy: SSH manager for clients & masters.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200487
488 Returns:
489 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400490 """
491 rp = self.manifest.repoProject
492
493 to_fetch = []
494 now = time.time()
495 if _ONE_DAY_S <= (now - rp.LastFetch):
496 to_fetch.append(rp)
497 to_fetch.extend(all_projects)
498 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
499
Mike Frysinger339f2df2021-05-06 00:44:42 -0400500 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400501 if not success:
502 err_event.set()
503
504 _PostRepoFetch(rp, opt.repo_verify)
505 if opt.network_only:
506 # bail out now; the rest touches the working tree
507 if err_event.is_set():
508 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
509 sys.exit(1)
510 return
511
512 # Iteratively fetch missing and/or nested unregistered submodules
513 previously_missing_set = set()
514 while True:
515 self._ReloadManifest(manifest_name, load_local_manifests)
516 all_projects = self.GetProjects(args,
517 missing_ok=True,
518 submodules_ok=opt.fetch_submodules)
519 missing = []
520 for project in all_projects:
521 if project.gitdir not in fetched:
522 missing.append(project)
523 if not missing:
524 break
525 # Stop us from non-stopped fetching actually-missing repos: If set of
526 # missing repos has not been changed from last fetch, we break.
527 missing_set = set(p.name for p in missing)
528 if previously_missing_set == missing_set:
529 break
530 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400531 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400532 if not success:
533 err_event.set()
534 fetched.update(new_fetched)
535
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200536 return all_projects
537
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500538 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700539 """Checkout work tree for one project
540
541 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500542 detach_head: Whether to leave a detached HEAD.
543 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700544 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700545
546 Returns:
547 Whether the fetch was successful.
548 """
Xin Li745be2e2019-06-03 11:24:30 -0700549 start = time.time()
550 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500551 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700552 success = False
553 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500554 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500555 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700556 except GitError as e:
557 print('error.GitError: Cannot checkout %s: %s' %
558 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500559 except Exception as e:
560 print('error: Cannot checkout %s: %s: %s' %
561 (project.name, type(e).__name__, str(e)),
562 file=sys.stderr)
563 raise
Xin Li745be2e2019-06-03 11:24:30 -0700564
Mike Frysingerebf04a42021-02-23 20:48:04 -0500565 if not success:
566 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
567 finish = time.time()
568 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700569
Mike Frysingerebf04a42021-02-23 20:48:04 -0500570 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700571 """Checkout projects listed in all_projects
572
573 Args:
574 all_projects: List of all projects that should be checked out.
575 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500576 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700577 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500578 # Only checkout projects with worktrees.
579 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700580
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500581 def _ProcessResults(pool, pm, results):
582 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500583 for (success, project, start, finish) in results:
584 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
585 start, finish, success)
586 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500587 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500588 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500589 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500590 err_results.append(project.relpath)
591 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500592 if pool:
593 pool.close()
594 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500595 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500596 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700597
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500598 return self.ExecuteInParallel(
599 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
600 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
601 all_projects,
602 callback=_ProcessResults,
603 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500604
Mike Frysinger5a033082019-09-23 19:21:20 -0400605 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400606 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400607 pm.update(inc=0, msg='prescan')
608
Allen Webb4ee4a452021-10-07 10:42:38 -0500609 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700610 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500611 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500612 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900613 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100614 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400615 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100616 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500617 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500618 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500619 else:
620 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400621 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500622 'versions of git; please upgrade to git-2.7.0+.'
623 % (project.relpath,),
624 file=sys.stderr)
625 project.config.SetString('gc.pruneExpire', 'never')
Allen Webb669efd02021-10-01 15:25:31 -0500626 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500627 # Only call git gc once per objdir, but call pack-refs for the remainder.
628 if project.objdir not in tidy_dirs:
629 tidy_dirs[project.objdir] = (
630 True, # Run a full gc.
631 project.bare_git,
632 )
633 elif project.gitdir not in tidy_dirs:
634 tidy_dirs[project.gitdir] = (
635 False, # Do not run a full gc; just run pack-refs.
636 project.bare_git,
637 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400638
639 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700640 jobs = min(self.jobs, cpu_count)
641
642 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500643 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400644 pm.update(msg=bare_git._project.name)
Allen Webb4ee4a452021-10-07 10:42:38 -0500645 if run_gc:
646 bare_git.gc('--auto')
647 else:
648 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400649 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700650 return
651
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400652 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700653
654 threads = set()
655 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700656
Allen Webb4ee4a452021-10-07 10:42:38 -0500657 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400658 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700659 try:
660 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500661 if run_gc:
662 bare_git.gc('--auto', config=config)
663 else:
664 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700665 except GitError:
666 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900667 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700668 err_event.set()
669 raise
670 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400671 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700672 sem.release()
673
Allen Webb4ee4a452021-10-07 10:42:38 -0500674 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500675 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700676 break
677 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500678 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700679 t.daemon = True
680 threads.add(t)
681 t.start()
682
683 for t in threads:
684 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400685 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700686
Raman Tennetifeb28912021-05-02 19:47:29 -0700687 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
688 """Reload the manfiest from the file specified by the |manifest_name|.
689
690 It unloads the manifest if |manifest_name| is None.
691
692 Args:
693 manifest_name: Manifest file to be reloaded.
694 load_local_manifests: Whether to load local manifests.
695 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800696 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000697 # Override calls Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700698 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800699 else:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000700 self.manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800701
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500702 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700703 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700704 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700705 if project.relpath:
706 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700707 file_name = 'project.list'
LaMont Jonescc879a92021-11-18 22:40:18 +0000708 file_path = os.path.join(self.manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700709 old_project_paths = []
710
711 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500712 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700713 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800714 # In reversed order, so subfolders are deleted before parent folder.
715 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700716 if not path:
717 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700718 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900719 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700720 gitdir = os.path.join(self.manifest.topdir, path, '.git')
721 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900722 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900723 manifest=self.manifest,
724 name=path,
725 remote=RemoteSpec('origin'),
726 gitdir=gitdir,
727 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500728 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900729 worktree=os.path.join(self.manifest.topdir, path),
730 relpath=path,
731 revisionExpr='HEAD',
732 revisionId=None,
733 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500734 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900735 quiet=opt.quiet,
736 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400737 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700738
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700739 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500740 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700741 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700742 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700743 return 0
744
jiajia tanga590e642021-04-25 20:02:02 +0800745 def UpdateCopyLinkfileList(self):
746 """Save all dests of copyfile and linkfile, and update them if needed.
747
748 Returns:
749 Whether update was successful.
750 """
751 new_paths = {}
752 new_linkfile_paths = []
753 new_copyfile_paths = []
754 for project in self.GetProjects(None, missing_ok=True):
755 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
756 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
757
758 new_paths = {
759 'linkfile': new_linkfile_paths,
760 'copyfile': new_copyfile_paths,
761 }
762
763 copylinkfile_name = 'copy-link-files.json'
LaMont Jonescc879a92021-11-18 22:40:18 +0000764 copylinkfile_path = os.path.join(self.manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +0800765 old_copylinkfile_paths = {}
766
767 if os.path.exists(copylinkfile_path):
768 with open(copylinkfile_path, 'rb') as fp:
769 try:
770 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800771 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +0800772 print('error: %s is not a json formatted file.' %
773 copylinkfile_path, file=sys.stderr)
774 platform_utils.remove(copylinkfile_path)
775 return False
776
777 need_remove_files = []
778 need_remove_files.extend(
779 set(old_copylinkfile_paths.get('linkfile', [])) -
780 set(new_linkfile_paths))
781 need_remove_files.extend(
782 set(old_copylinkfile_paths.get('copyfile', [])) -
783 set(new_copyfile_paths))
784
785 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -0400786 # Try to remove the updated copyfile or linkfile.
787 # So, if the file is not exist, nothing need to do.
788 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +0800789
790 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
791 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
792 json.dump(new_paths, fp)
793 return True
794
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400795 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
796 if not self.manifest.manifest_server:
797 print('error: cannot smart sync: no manifest server defined in '
798 'manifest', file=sys.stderr)
799 sys.exit(1)
800
801 manifest_server = self.manifest.manifest_server
802 if not opt.quiet:
803 print('Using manifest server %s' % manifest_server)
804
David Pursehouseeeff3532020-02-12 11:24:10 +0900805 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400806 username = None
807 password = None
808 if opt.manifest_server_username and opt.manifest_server_password:
809 username = opt.manifest_server_username
810 password = opt.manifest_server_password
811 else:
812 try:
813 info = netrc.netrc()
814 except IOError:
815 # .netrc file does not exist or could not be opened
816 pass
817 else:
818 try:
819 parse_result = urllib.parse.urlparse(manifest_server)
820 if parse_result.hostname:
821 auth = info.authenticators(parse_result.hostname)
822 if auth:
823 username, _account, password = auth
824 else:
825 print('No credentials found for %s in .netrc'
826 % parse_result.hostname, file=sys.stderr)
827 except netrc.NetrcParseError as e:
828 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
829
830 if (username and password):
831 manifest_server = manifest_server.replace('://', '://%s:%s@' %
832 (username, password),
833 1)
834
835 transport = PersistentTransport(manifest_server)
836 if manifest_server.startswith('persistent-'):
837 manifest_server = manifest_server[len('persistent-'):]
838
839 try:
840 server = xmlrpc.client.Server(manifest_server, transport=transport)
841 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800842 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400843
Mike Frysinger56ce3462019-12-04 19:30:48 -0500844 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500845 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400846 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500847 elif ('TARGET_PRODUCT' in os.environ and
848 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500849 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
850 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400851 [success, manifest_str] = server.GetApprovedManifest(branch, target)
852 else:
853 [success, manifest_str] = server.GetApprovedManifest(branch)
854 else:
855 assert(opt.smart_tag)
856 [success, manifest_str] = server.GetManifest(opt.smart_tag)
857
858 if success:
859 manifest_name = os.path.basename(smart_sync_manifest_path)
860 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500861 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400862 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400863 except IOError as e:
864 print('error: cannot write manifest to %s:\n%s'
865 % (smart_sync_manifest_path, e),
866 file=sys.stderr)
867 sys.exit(1)
868 self._ReloadManifest(manifest_name)
869 else:
870 print('error: manifest server RPC call failed: %s' %
871 manifest_str, file=sys.stderr)
872 sys.exit(1)
873 except (socket.error, IOError, xmlrpc.client.Fault) as e:
874 print('error: cannot connect to manifest server %s:\n%s'
875 % (self.manifest.manifest_server, e), file=sys.stderr)
876 sys.exit(1)
877 except xmlrpc.client.ProtocolError as e:
878 print('error: cannot connect to manifest server %s:\n%d %s'
879 % (self.manifest.manifest_server, e.errcode, e.errmsg),
880 file=sys.stderr)
881 sys.exit(1)
882
883 return manifest_name
884
Mike Frysingerfb527e32019-08-27 02:34:32 -0400885 def _UpdateManifestProject(self, opt, mp, manifest_name):
886 """Fetch & update the local manifest project."""
887 if not opt.local_only:
888 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500889 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700890 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200891 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500892 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400893 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600894 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400895 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700896 clone_filter=self.manifest.CloneFilter,
897 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400898 finish = time.time()
899 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
900 start, finish, success)
901
902 if mp.HasChanges:
903 syncbuf = SyncBuffer(mp.config)
904 start = time.time()
905 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
906 clean = syncbuf.Finish()
907 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
908 start, time.time(), clean)
909 if not clean:
910 sys.exit(1)
Mike Frysinger05638bf2021-05-04 15:33:31 -0400911 self._ReloadManifest(manifest_name)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400912 if opt.jobs is None:
913 self.jobs = self.manifest.default.sync_j
914
Mike Frysingerae6cb082019-08-27 01:10:59 -0400915 def ValidateOptions(self, opt, args):
916 if opt.force_broken:
917 print('warning: -f/--force-broken is now the default behavior, and the '
918 'options are deprecated', file=sys.stderr)
919 if opt.network_only and opt.detach_head:
920 self.OptionParser.error('cannot combine -n and -d')
921 if opt.network_only and opt.local_only:
922 self.OptionParser.error('cannot combine -n and -l')
923 if opt.manifest_name and opt.smart_sync:
924 self.OptionParser.error('cannot combine -m and -s')
925 if opt.manifest_name and opt.smart_tag:
926 self.OptionParser.error('cannot combine -m and -t')
927 if opt.manifest_server_username or opt.manifest_server_password:
928 if not (opt.smart_sync or opt.smart_tag):
929 self.OptionParser.error('-u and -p may only be combined with -s or -t')
930 if None in [opt.manifest_server_username, opt.manifest_server_password]:
931 self.OptionParser.error('both -u and -p must be given')
932
Mike Frysinger0531a622021-11-05 15:22:01 -0400933 if opt.prune is None:
934 opt.prune = True
935
LaMont Jonescc879a92021-11-18 22:40:18 +0000936 if self.manifest.is_multimanifest and not opt.this_manifest_only and args:
937 self.OptionParser.error('partial syncs must use --this-manifest-only')
938
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700939 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800940 if opt.jobs:
941 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700942 if self.jobs > 1:
943 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400944 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700945
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500946 if opt.manifest_name:
947 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700948
Chirayu Desaia892b102013-06-11 14:18:46 +0530949 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900950 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900951 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530952
Xin Lid79a4bc2020-05-20 16:03:45 -0700953 if opt.clone_bundle is None:
954 opt.clone_bundle = self.manifest.CloneBundle
955
Victor Boivie08c880d2011-04-19 10:32:52 +0200956 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400957 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
958 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900959 if os.path.isfile(smart_sync_manifest_path):
960 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800961 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900962 except OSError as e:
963 print('error: failed to remove existing smart sync override manifest: %s' %
964 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700965
Mike Frysingerc99322a2021-05-04 15:32:43 -0400966 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400967
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700968 rp = self.manifest.repoProject
969 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500970 cb = rp.CurrentBranch
971 if cb:
972 base = rp.GetBranch(cb).merge
973 if not base or not base.startswith('refs/heads/'):
974 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400975 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500976 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700977
978 mp = self.manifest.manifestProject
LaMont Jonesd82be3e2022-04-05 19:30:46 +0000979 is_standalone_manifest = bool(mp.standalone_manifest_url)
Jack Neus03ff2762021-10-15 15:43:19 +0000980 if not is_standalone_manifest:
981 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700982
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800983 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700984 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800985
Fredrik de Grootcc960972019-11-22 09:04:31 +0100986 if not opt.mp_update:
987 print('Skipping update of local manifest project.')
Jack Neus03ff2762021-10-15 15:43:19 +0000988 elif not is_standalone_manifest:
Fredrik de Grootcc960972019-11-22 09:04:31 +0100989 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700990
Raman Tennetifeb28912021-05-02 19:47:29 -0700991 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti7954de12021-07-28 14:36:49 -0700992 use_superproject = git_superproject.UseSuperproject(opt, self.manifest)
Raman Tenneticf0ba482021-12-06 18:12:59 -0800993 if use_superproject and (self.manifest.IsMirror or self.manifest.IsArchive):
Raman Tenneti6bd89aa2021-11-16 11:48:09 -0800994 # Don't use superproject, because we have no working tree.
995 use_superproject = False
Raman Tenneticf0ba482021-12-06 18:12:59 -0800996 if opt.use_superproject is not None:
997 print('Defaulting to no-use-superproject because there is no working tree.')
Raman Tenneti7954de12021-07-28 14:36:49 -0700998 superproject_logging_data = {
999 'superproject': use_superproject,
1000 'haslocalmanifests': bool(self.manifest.HasLocalManifests),
Raman Tennetib55769a2021-08-13 11:47:24 -07001001 'hassuperprojecttag': bool(self.manifest.superproject),
Raman Tenneti7954de12021-07-28 14:36:49 -07001002 }
1003 if use_superproject:
1004 manifest_name = self._UpdateProjectsRevisionId(
1005 opt, args, load_local_manifests, superproject_logging_data) or opt.manifest_name
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001006
Simran Basib9a1b732015-08-20 12:19:28 -07001007 if self.gitc_manifest:
1008 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -07001009 missing_ok=True)
1010 gitc_projects = []
1011 opened_projects = []
1012 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001013 if project.relpath in self.gitc_manifest.paths and \
1014 self.gitc_manifest.paths[project.relpath].old_revision:
1015 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001016 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001017 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001018
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001019 if not args:
1020 gitc_projects = None
1021
1022 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001023 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001024 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1025 if manifest_name:
1026 manifest.Override(manifest_name)
1027 else:
1028 manifest.Override(self.manifest.manifestFile)
1029 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1030 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001031 gitc_projects)
1032 print('GITC client successfully synced.')
1033
1034 # The opened projects need to be synced as normal, therefore we
1035 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001036 # TODO: make this more reliable -- if there's a project name/path overlap,
1037 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +09001038 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
1039 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001040 if not args:
1041 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001042 all_projects = self.GetProjects(args,
1043 missing_ok=True,
1044 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001045
Mike Frysinger5a033082019-09-23 19:21:20 -04001046 err_network_sync = False
1047 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001048
Dave Borowitz67700e92012-10-23 15:00:54 -07001049 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001050 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001051 with multiprocessing.Manager() as manager:
1052 with ssh.ProxyManager(manager) as ssh_proxy:
1053 # Initialize the socket dir once in the parent.
1054 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001055 all_projects = self._FetchMain(opt, args, all_projects, err_event,
1056 manifest_name, load_local_manifests,
1057 ssh_proxy)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001058
1059 if opt.network_only:
1060 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001061
1062 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001063 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001064 err_network_sync = True
1065 if opt.fail_fast:
1066 print('\nerror: Exited sync due to fetch errors.\n'
1067 'Local checkouts *not* updated. Resolve network issues & '
1068 'retry.\n'
1069 '`repo sync -l` will update some local checkouts.',
1070 file=sys.stderr)
1071 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001072
Julien Campergue335f5ef2013-10-16 11:02:35 +02001073 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001074 # bail out now, we have no working tree
1075 return
1076
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001077 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001078 err_event.set()
1079 err_update_projects = True
1080 if opt.fail_fast:
1081 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1082 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001083
Mike Frysinger14208f42021-05-04 15:31:51 -04001084 err_update_linkfiles = not self.UpdateCopyLinkfileList()
1085 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001086 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001087 if opt.fail_fast:
1088 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1089 sys.exit(1)
1090
Mike Frysinger5a033082019-09-23 19:21:20 -04001091 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001092 # NB: We don't exit here because this is the last step.
1093 err_checkout = not self._Checkout(all_projects, opt, err_results)
1094 if err_checkout:
1095 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001096
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001097 # If there's a notice that's supposed to print at the end of the sync, print
1098 # it now...
1099 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001100 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001101
Mike Frysinger5a033082019-09-23 19:21:20 -04001102 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001103 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001104 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1105 if err_network_sync:
1106 print('error: Downloading network changes failed.', file=sys.stderr)
1107 if err_update_projects:
1108 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001109 if err_update_linkfiles:
1110 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001111 if err_checkout:
1112 print('error: Checking out local projects failed.', file=sys.stderr)
1113 if err_results:
1114 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1115 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1116 file=sys.stderr)
1117 sys.exit(1)
1118
Raman Tenneti7954de12021-07-28 14:36:49 -07001119 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001120 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1121 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001122
1123 # Update and log with the new sync analysis state.
1124 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001125 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1126 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001127
Mike Frysingere19d9e12020-02-12 11:23:32 -05001128 if not opt.quiet:
1129 print('repo sync has finished successfully.')
1130
David Pursehouse819827a2020-02-12 15:20:19 +09001131
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001132def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001133 # Link the docs for the internal .repo/ layout for people
1134 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1135 if not platform_utils.islink(link):
1136 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1137 try:
1138 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001139 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001140 pass
1141
Conley Owens094cdbe2014-01-30 15:09:59 -08001142 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001143 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001144 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001145 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001146 if project.Exists:
1147 project.PostRepoUpgrade()
1148
David Pursehouse819827a2020-02-12 15:20:19 +09001149
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001150def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001151 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001152 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001153 wrapper = Wrapper()
1154 try:
1155 rev = rp.bare_git.describe(rp.GetRevisionId())
1156 except GitError:
1157 rev = None
1158 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1159 # See if we're held back due to missing signed tag.
1160 current_revid = rp.bare_git.rev_parse('HEAD')
1161 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1162 if current_revid != new_revid:
1163 # We want to switch to the new rev, but also not trash any uncommitted
1164 # changes. This helps with local testing/hacking.
1165 # If a local change has been made, we will throw that away.
1166 # We also have to make sure this will switch to an older commit if that's
1167 # the latest tag in order to support release rollback.
1168 try:
1169 rp.work_git.reset('--keep', new_rev)
1170 except GitError as e:
1171 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001172 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001173 raise RepoChangedException(['--repo-upgraded'])
1174 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001175 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001176 else:
1177 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001178 print('repo version %s is current' % rp.work_git.describe(HEAD),
1179 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001180
David Pursehouse819827a2020-02-12 15:20:19 +09001181
Dave Borowitz67700e92012-10-23 15:00:54 -07001182class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001183 _ALPHA = 0.5
1184
Dave Borowitz67700e92012-10-23 15:00:54 -07001185 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001186 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001187 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001188 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001189
1190 def Get(self, project):
1191 self._Load()
1192 return self._times.get(project.name, _ONE_DAY_S)
1193
1194 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001195 self._Load()
1196 name = project.name
1197 old = self._times.get(name, t)
1198 self._seen.add(name)
1199 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001200 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001201
1202 def _Load(self):
1203 if self._times is None:
1204 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001205 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001206 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001207 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001208 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001209 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001210
1211 def Save(self):
1212 if self._times is None:
1213 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001214
1215 to_delete = []
1216 for name in self._times:
1217 if name not in self._seen:
1218 to_delete.append(name)
1219 for name in to_delete:
1220 del self._times[name]
1221
Dave Borowitz67700e92012-10-23 15:00:54 -07001222 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001223 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001224 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001225 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001226 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001227
1228# This is a replacement for xmlrpc.client.Transport using urllib2
1229# and supporting persistent-http[s]. It cannot change hosts from
1230# request to request like the normal transport, the real url
1231# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001232
1233
Dan Willemsen0745bb22015-08-17 13:41:45 -07001234class PersistentTransport(xmlrpc.client.Transport):
1235 def __init__(self, orig_host):
1236 self.orig_host = orig_host
1237
1238 def request(self, host, handler, request_body, verbose=False):
1239 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1240 # Python doesn't understand cookies with the #HttpOnly_ prefix
1241 # Since we're only using them for HTTP, copy the file temporarily,
1242 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001243 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001244 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001245 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001246 try:
1247 with open(cookiefile) as f:
1248 for line in f:
1249 if line.startswith("#HttpOnly_"):
1250 line = line[len("#HttpOnly_"):]
1251 tmpcookiefile.write(line)
1252 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001253
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001254 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001255 try:
1256 cookiejar.load()
1257 except cookielib.LoadError:
1258 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001259 finally:
1260 tmpcookiefile.close()
1261 else:
1262 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001263
1264 proxyhandler = urllib.request.ProxyHandler
1265 if proxy:
1266 proxyhandler = urllib.request.ProxyHandler({
1267 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001268 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001269
1270 opener = urllib.request.build_opener(
1271 urllib.request.HTTPCookieProcessor(cookiejar),
1272 proxyhandler)
1273
1274 url = urllib.parse.urljoin(self.orig_host, handler)
1275 parse_results = urllib.parse.urlparse(url)
1276
1277 scheme = parse_results.scheme
1278 if scheme == 'persistent-http':
1279 scheme = 'http'
1280 if scheme == 'persistent-https':
1281 # If we're proxying through persistent-https, use http. The
1282 # proxy itself will do the https.
1283 if proxy:
1284 scheme = 'http'
1285 else:
1286 scheme = 'https'
1287
1288 # Parse out any authentication information using the base class
1289 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1290
1291 url = urllib.parse.urlunparse((
1292 scheme,
1293 host,
1294 parse_results.path,
1295 parse_results.params,
1296 parse_results.query,
1297 parse_results.fragment))
1298
1299 request = urllib.request.Request(url, request_body)
1300 if extra_headers is not None:
1301 for (name, header) in extra_headers:
1302 request.add_header(name, header)
1303 request.add_header('Content-Type', 'text/xml')
1304 try:
1305 response = opener.open(request)
1306 except urllib.error.HTTPError as e:
1307 if e.code == 501:
1308 # We may have been redirected through a login process
1309 # but our POST turned into a GET. Retry.
1310 response = opener.open(request)
1311 else:
1312 raise
1313
1314 p, u = xmlrpc.client.getparser()
1315 while 1:
1316 data = response.read(1024)
1317 if not data:
1318 break
1319 p.feed(data)
1320 p.close()
1321 return u.close()
1322
1323 def close(self):
1324 pass