blob: 3451ab6b452317feedfe49991130c49bdbe93d5e [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 [<project>...]
73"""
74 helpDescription = """
75The '%prog' command synchronizes local project directories
76with the remote repositories specified in the manifest. If a local
77project does not yet exist, it will clone a new local directory from
78the remote repository and set up tracking branches as specified in
79the manifest. If the local project already exists, '%prog'
80will update the remote branches and rebase any new local changes
81on top of the new remote changes.
82
83'%prog' will synchronize all projects listed at the command
84line. Projects can be specified either by name, or by a relative
85or absolute path to the project's local directory. If no projects
86are specified, '%prog' will synchronize all projects listed in
87the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070088
89The -d/--detach option can be used to switch specified projects
90back to the manifest revision. This option is especially helpful
91if the project is currently on a topic branch, but the manifest
92revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -070093
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070094The -s/--smart-sync option can be used to sync to a known good
95build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +020096manifest. The -t/--smart-tag option is similar and allows you to
97specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070098
David Pursehousecf76b1b2012-09-14 10:31:42 +090099The -u/--manifest-server-username and -p/--manifest-server-password
100options can be used to specify a username and password to authenticate
101with the manifest server when using the -s or -t option.
102
103If -u and -p are not specified when using the -s or -t option, '%prog'
104will attempt to read authentication credentials for the manifest server
105from the user's .netrc file.
106
107'%prog' will not use authentication credentials from -u/-p or .netrc
108if the manifest server specified in the manifest file already includes
109credentials.
110
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400111By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400112to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500113
Kevin Degiabaa7f32014-11-12 11:27:45 -0700114The --force-sync option can be used to overwrite existing git
115directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900116object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700117refs may be removed when overwriting.
118
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500119The --force-remove-dirty option can be used to remove previously used
120projects with uncommitted changes. WARNING: This may cause data to be
121lost since uncommitted changes may be removed with projects that no longer
122exist in the manifest.
123
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700124The --no-clone-bundle option disables any attempt to use
125$URL/clone.bundle to bootstrap a new Git repository from a
126resumeable bundle file on a content delivery network. This
127may be necessary if there are problems with the local Python
128HTTP client or proxy configuration, but the Git binary works.
129
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800130The --fetch-submodules option enables fetching Git submodules
131of a project from server.
132
David Pursehousef2fad612015-01-29 14:36:28 +0900133The -c/--current-branch option can be used to only fetch objects that
134are on the branch specified by a project's revision.
135
David Pursehouseb1553542014-09-04 21:28:09 +0900136The --optimized-fetch option can be used to only fetch projects that
137are fixed to a sha1 revision if the sha1 revision does not already
138exist locally.
139
David Pursehouse74cfd272015-10-14 10:50:15 +0900140The --prune option can be used to remove any refs that no longer
141exist on the remote.
142
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400143# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700144
145If at least one project remote URL uses an SSH connection (ssh://,
146git+ssh://, or user@host:path syntax) repo will automatically
147enable the SSH ControlMaster option when connecting to that host.
148This feature permits other projects in the same '%prog' session to
149reuse the same SSH tunnel, saving connection setup overheads.
150
151To disable this behavior on UNIX platforms, set the GIT_SSH
152environment variable to 'ssh'. For example:
153
154 export GIT_SSH=ssh
155 %prog
156
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400157# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700158
159This feature is automatically disabled on Windows, due to the lack
160of UNIX domain socket support.
161
162This feature is not compatible with url.insteadof rewrites in the
163user's ~/.gitconfig. '%prog' is currently not able to perform the
164rewrite early enough to establish the ControlMaster tunnel.
165
166If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
167later is required to fix a server side protocol bug.
168
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700169"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500170 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700171
Mike Frysinger9180a072021-04-13 14:57:40 -0400172 def _CommonOptions(self, p):
Mike Frysingerc177f942021-05-04 08:06:36 -0400173 if self.manifest:
174 try:
175 self.PARALLEL_JOBS = self.manifest.default.sync_j
176 except ManifestParseError:
177 pass
Mike Frysinger9180a072021-04-13 14:57:40 -0400178 super()._CommonOptions(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700179
Mike Frysinger9180a072021-04-13 14:57:40 -0400180 def _Options(self, p, show_smart=True):
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400181 p.add_option('--jobs-network', default=None, type=int, metavar='JOBS',
182 help='number of network jobs to run in parallel (defaults to --jobs)')
183 p.add_option('--jobs-checkout', default=None, type=int, metavar='JOBS',
184 help='number of local checkout jobs to run in parallel (defaults to --jobs)')
185
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500186 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200187 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400188 help='obsolete option (to be deleted in the future)')
189 p.add_option('--fail-fast',
190 dest='fail_fast', action='store_true',
191 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700192 p.add_option('--force-sync',
193 dest='force_sync', action='store_true',
194 help="overwrite an existing git directory if it needs to "
195 "point to a different object directory. WARNING: this "
196 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500197 p.add_option('--force-remove-dirty',
198 dest='force_remove_dirty', action='store_true',
199 help="force remove projects with uncommitted modifications if "
200 "projects no longer exist in the manifest. "
201 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900202 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700203 dest='local_only', action='store_true',
204 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900205 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100206 dest='mp_update', action='store_false', default='true',
207 help='use the existing manifest checkout as-is. '
208 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900209 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700210 dest='network_only', action='store_true',
211 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900212 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700213 dest='detach_head', action='store_true',
214 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900215 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700216 dest='current_branch_only', action='store_true',
217 help='fetch only current branch from server')
Mike Frysinger73561142021-05-03 01:10:09 -0400218 p.add_option('--no-current-branch',
219 dest='current_branch_only', action='store_false',
220 help='fetch all branches from server')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500221 p.add_option('-m', '--manifest-name',
222 dest='manifest_name',
223 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700224 p.add_option('--clone-bundle', action='store_true',
225 help='enable use of /clone.bundle on HTTP/HTTPS')
226 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700227 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800228 p.add_option('-u', '--manifest-server-username', action='store',
229 dest='manifest_server_username',
230 help='username to authenticate with the manifest server')
231 p.add_option('-p', '--manifest-server-password', action='store',
232 dest='manifest_server_password',
233 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800234 p.add_option('--fetch-submodules',
235 dest='fetch_submodules', action='store_true',
236 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800237 p.add_option('--use-superproject', action='store_true',
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):
Daniel Anderssond52ca422022-04-01 12:55:38 +0200283 """Returns whether current-branch or use-superproject options are enabled.
284
285 Returns:
286 True if a superproject is requested, otherwise the value of the
287 current_branch option (True, False or None).
288 """
LaMont Jones5fa912b2022-04-14 14:41:13 +0000289 return git_superproject.UseSuperproject(opt.use_superproject, self.manifest) or opt.current_branch_only
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700290
Raman Tenneti7954de12021-07-28 14:36:49 -0700291 def _UpdateProjectsRevisionId(self, opt, args, load_local_manifests, superproject_logging_data):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800292 """Update revisionId of every project with the SHA from superproject.
293
294 This function updates each project's revisionId with SHA from superproject.
295 It writes the updated manifest into a file and reloads the manifest from it.
296
297 Args:
298 opt: Program options returned from optparse. See _Options().
299 args: Arguments to pass to GetProjects. See the GetProjects
300 docstring for details.
Raman Tennetifeb28912021-05-02 19:47:29 -0700301 load_local_manifests: Whether to load local manifests.
Raman Tenneti7954de12021-07-28 14:36:49 -0700302 superproject_logging_data: A dictionary of superproject data that is to be logged.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800303
304 Returns:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700305 Returns path to the overriding manifest file instead of None.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800306 """
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000307 superproject = self.manifest.superproject
308 superproject.SetQuiet(opt.quiet)
LaMont Jones5fa912b2022-04-14 14:41:13 +0000309 print_messages = git_superproject.PrintMessages(opt.use_superproject,
310 self.manifest)
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000311 superproject.SetPrintMessages(print_messages)
Raman Tennetiae86a462021-07-27 08:54:59 -0700312 if opt.local_only:
313 manifest_path = superproject.manifest_path
314 if manifest_path:
315 self._ReloadManifest(manifest_path, load_local_manifests)
316 return manifest_path
317
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800318 all_projects = self.GetProjects(args,
319 missing_ok=True,
320 submodules_ok=opt.fetch_submodules)
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000321 update_result = superproject.UpdateProjectsRevisionId(
322 all_projects, git_event_log=self.git_event_log)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700323 manifest_path = update_result.manifest_path
Raman Tenneti7954de12021-07-28 14:36:49 -0700324 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700325 if manifest_path:
326 self._ReloadManifest(manifest_path, load_local_manifests)
327 else:
Raman Tennetib55769a2021-08-13 11:47:24 -0700328 if print_messages:
329 print('warning: Update of revisionId from superproject has failed, '
330 'repo sync will not use superproject to fetch the source. ',
331 'Please resync with the --no-use-superproject option to avoid this repo warning.',
332 file=sys.stderr)
Raman Tenneti8db30d62021-07-06 21:30:06 -0700333 if update_result.fatal and opt.use_superproject is not None:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700334 sys.exit(1)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800335 return manifest_path
336
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500337 def _FetchProjectList(self, opt, projects):
338 """Main function of the fetch worker.
339
340 The projects we're given share the same underlying git object store, so we
341 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800342
David James8d201162013-10-11 17:03:19 -0700343 Delegates most of the work to _FetchHelper.
344
345 Args:
346 opt: Program options returned from optparse. See _Options().
347 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700348 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500349 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700350
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500351 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700352 """Fetch git objects for a single project.
353
David Pursehousec1b86a22012-11-14 11:36:51 +0900354 Args:
355 opt: Program options returned from optparse. See _Options().
356 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700357
358 Returns:
359 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900360 """
David Rileye0684ad2017-04-05 00:02:59 -0700361 start = time.time()
362 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500363 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900364 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500365 success = project.Sync_NetworkHalf(
366 quiet=opt.quiet,
367 verbose=opt.verbose,
368 output_redir=buf,
369 current_branch_only=self._GetCurrentBranchOnly(opt),
370 force_sync=opt.force_sync,
371 clone_bundle=opt.clone_bundle,
372 tags=opt.tags, archive=self.manifest.IsArchive,
373 optimized_fetch=opt.optimized_fetch,
374 retry_fetches=opt.retry_fetches,
375 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400376 ssh_proxy=self.ssh_proxy,
Raman Tennetif32f2432021-04-12 20:57:25 -0700377 clone_filter=self.manifest.CloneFilter,
378 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700379
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500380 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400381 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500382 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700383
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500384 if not success:
385 print('error: Cannot fetch %s from %s'
386 % (project.name, project.remote.url),
387 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700388 except GitError as e:
389 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500390 except Exception as e:
391 print('error: Cannot fetch %s (%s: %s)'
392 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
393 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500394
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500395 finish = time.time()
396 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700397
Mike Frysinger339f2df2021-05-06 00:44:42 -0400398 @classmethod
399 def _FetchInitChild(cls, ssh_proxy):
400 cls.ssh_proxy = ssh_proxy
401
402 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500403 ret = True
404
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400405 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700406 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400407 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800408
David James89ece422014-01-09 18:51:58 -0800409 objdir_project_map = dict()
410 for project in projects:
411 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500412 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700413
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500414 def _ProcessResults(results_sets):
415 ret = True
416 for results in results_sets:
417 for (success, project, start, finish) in results:
418 self._fetch_times.Set(project, finish - start)
419 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
420 start, finish, success)
421 # Check for any errors before running any more tasks.
422 # ...we'll let existing jobs finish, though.
423 if not success:
424 ret = False
425 else:
426 fetched.add(project.gitdir)
427 pm.update(msg=project.name)
428 if not ret and opt.fail_fast:
429 break
430 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700431
Mike Frysinger339f2df2021-05-06 00:44:42 -0400432 # We pass the ssh proxy settings via the class. This allows multiprocessing
433 # to pickle it up when spawning children. We can't pass it as an argument
434 # to _FetchProjectList below as multiprocessing is unable to pickle those.
435 Sync.ssh_proxy = None
436
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500437 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400438 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400439 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500440 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
441 ret = False
442 else:
443 # Favor throughput over responsiveness when quiet. It seems that imap()
444 # will yield results in batches relative to chunksize, so even as the
445 # children finish a sync, we won't see the result until one child finishes
446 # ~chunksize jobs. When using a large --jobs with large chunksize, this
447 # can be jarring as there will be a large initial delay where repo looks
448 # like it isn't doing anything and sits at 0%, but then suddenly completes
449 # a lot of jobs all at once. Since this code is more network bound, we
450 # can accept a bit more CPU overhead with a smaller chunksize so that the
451 # user sees more immediate & continuous feedback.
452 if opt.quiet:
453 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800454 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500455 pm.update(inc=0, msg='warming up')
456 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800457 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
458 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500459 results = pool.imap_unordered(
460 functools.partial(self._FetchProjectList, opt),
461 projects_list,
462 chunksize=chunksize)
463 if not _ProcessResults(results):
464 ret = False
465 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800466
Mike Frysinger339f2df2021-05-06 00:44:42 -0400467 # Cleanup the reference now that we're done with it, and we're going to
468 # release any resources it points to. If we don't, later multiprocessing
469 # usage (e.g. checkouts) will try to pickle and then crash.
470 del Sync.ssh_proxy
471
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700472 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700473 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700474
Julien Campergue335f5ef2013-10-16 11:02:35 +0200475 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400476 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200477
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500478 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700479
Mike Frysingerb4429432021-05-05 20:03:26 -0400480 def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400481 load_local_manifests, ssh_proxy):
Mike Frysingerb4429432021-05-05 20:03:26 -0400482 """The main network fetch loop.
483
484 Args:
485 opt: Program options returned from optparse. See _Options().
486 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200487 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400488 err_event: Whether an error was hit while processing.
489 manifest_name: Manifest file to be reloaded.
490 load_local_manifests: Whether to load local manifests.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400491 ssh_proxy: SSH manager for clients & masters.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200492
493 Returns:
494 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400495 """
496 rp = self.manifest.repoProject
497
498 to_fetch = []
499 now = time.time()
500 if _ONE_DAY_S <= (now - rp.LastFetch):
501 to_fetch.append(rp)
502 to_fetch.extend(all_projects)
503 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
504
Mike Frysinger339f2df2021-05-06 00:44:42 -0400505 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400506 if not success:
507 err_event.set()
508
509 _PostRepoFetch(rp, opt.repo_verify)
510 if opt.network_only:
511 # bail out now; the rest touches the working tree
512 if err_event.is_set():
513 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
514 sys.exit(1)
515 return
516
517 # Iteratively fetch missing and/or nested unregistered submodules
518 previously_missing_set = set()
519 while True:
520 self._ReloadManifest(manifest_name, load_local_manifests)
521 all_projects = self.GetProjects(args,
522 missing_ok=True,
523 submodules_ok=opt.fetch_submodules)
524 missing = []
525 for project in all_projects:
526 if project.gitdir not in fetched:
527 missing.append(project)
528 if not missing:
529 break
530 # Stop us from non-stopped fetching actually-missing repos: If set of
531 # missing repos has not been changed from last fetch, we break.
532 missing_set = set(p.name for p in missing)
533 if previously_missing_set == missing_set:
534 break
535 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400536 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400537 if not success:
538 err_event.set()
539 fetched.update(new_fetched)
540
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200541 return all_projects
542
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500543 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700544 """Checkout work tree for one project
545
546 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500547 detach_head: Whether to leave a detached HEAD.
548 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700549 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700550
551 Returns:
552 Whether the fetch was successful.
553 """
Xin Li745be2e2019-06-03 11:24:30 -0700554 start = time.time()
555 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500556 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700557 success = False
558 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500559 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500560 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700561 except GitError as e:
562 print('error.GitError: Cannot checkout %s: %s' %
563 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500564 except Exception as e:
565 print('error: Cannot checkout %s: %s: %s' %
566 (project.name, type(e).__name__, str(e)),
567 file=sys.stderr)
568 raise
Xin Li745be2e2019-06-03 11:24:30 -0700569
Mike Frysingerebf04a42021-02-23 20:48:04 -0500570 if not success:
571 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
572 finish = time.time()
573 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700574
Mike Frysingerebf04a42021-02-23 20:48:04 -0500575 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700576 """Checkout projects listed in all_projects
577
578 Args:
579 all_projects: List of all projects that should be checked out.
580 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500581 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700582 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500583 # Only checkout projects with worktrees.
584 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700585
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500586 def _ProcessResults(pool, pm, results):
587 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500588 for (success, project, start, finish) in results:
589 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
590 start, finish, success)
591 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500592 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500593 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500594 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500595 err_results.append(project.relpath)
596 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500597 if pool:
598 pool.close()
599 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500600 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500601 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700602
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500603 return self.ExecuteInParallel(
604 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
605 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
606 all_projects,
607 callback=_ProcessResults,
608 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500609
Mike Frysinger5a033082019-09-23 19:21:20 -0400610 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400611 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400612 pm.update(inc=0, msg='prescan')
613
Allen Webb4ee4a452021-10-07 10:42:38 -0500614 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700615 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500616 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500617 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900618 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100619 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400620 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100621 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500622 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500623 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500624 else:
625 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400626 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500627 'versions of git; please upgrade to git-2.7.0+.'
628 % (project.relpath,),
629 file=sys.stderr)
630 project.config.SetString('gc.pruneExpire', 'never')
Allen Webb669efd02021-10-01 15:25:31 -0500631 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500632 # Only call git gc once per objdir, but call pack-refs for the remainder.
633 if project.objdir not in tidy_dirs:
634 tidy_dirs[project.objdir] = (
635 True, # Run a full gc.
636 project.bare_git,
637 )
638 elif project.gitdir not in tidy_dirs:
639 tidy_dirs[project.gitdir] = (
640 False, # Do not run a full gc; just run pack-refs.
641 project.bare_git,
642 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400643
644 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700645 jobs = min(self.jobs, cpu_count)
646
647 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500648 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400649 pm.update(msg=bare_git._project.name)
Allen Webb4ee4a452021-10-07 10:42:38 -0500650 if run_gc:
651 bare_git.gc('--auto')
652 else:
653 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400654 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700655 return
656
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400657 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700658
659 threads = set()
660 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700661
Allen Webb4ee4a452021-10-07 10:42:38 -0500662 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400663 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700664 try:
665 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500666 if run_gc:
667 bare_git.gc('--auto', config=config)
668 else:
669 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700670 except GitError:
671 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900672 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700673 err_event.set()
674 raise
675 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400676 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700677 sem.release()
678
Allen Webb4ee4a452021-10-07 10:42:38 -0500679 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500680 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700681 break
682 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500683 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700684 t.daemon = True
685 threads.add(t)
686 t.start()
687
688 for t in threads:
689 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400690 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700691
Raman Tennetifeb28912021-05-02 19:47:29 -0700692 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
693 """Reload the manfiest from the file specified by the |manifest_name|.
694
695 It unloads the manifest if |manifest_name| is None.
696
697 Args:
698 manifest_name: Manifest file to be reloaded.
699 load_local_manifests: Whether to load local manifests.
700 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800701 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000702 # Override calls Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700703 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800704 else:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000705 self.manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800706
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500707 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700708 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700709 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700710 if project.relpath:
711 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700712 file_name = 'project.list'
LaMont Jonescc879a92021-11-18 22:40:18 +0000713 file_path = os.path.join(self.manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700714 old_project_paths = []
715
716 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500717 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700718 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800719 # In reversed order, so subfolders are deleted before parent folder.
720 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700721 if not path:
722 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700723 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900724 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700725 gitdir = os.path.join(self.manifest.topdir, path, '.git')
726 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900727 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900728 manifest=self.manifest,
729 name=path,
730 remote=RemoteSpec('origin'),
731 gitdir=gitdir,
732 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500733 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900734 worktree=os.path.join(self.manifest.topdir, path),
735 relpath=path,
736 revisionExpr='HEAD',
737 revisionId=None,
738 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500739 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900740 quiet=opt.quiet,
741 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400742 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700743
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700744 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500745 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700746 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700747 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700748 return 0
749
jiajia tanga590e642021-04-25 20:02:02 +0800750 def UpdateCopyLinkfileList(self):
751 """Save all dests of copyfile and linkfile, and update them if needed.
752
753 Returns:
754 Whether update was successful.
755 """
756 new_paths = {}
757 new_linkfile_paths = []
758 new_copyfile_paths = []
759 for project in self.GetProjects(None, missing_ok=True):
760 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
761 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
762
763 new_paths = {
764 'linkfile': new_linkfile_paths,
765 'copyfile': new_copyfile_paths,
766 }
767
768 copylinkfile_name = 'copy-link-files.json'
LaMont Jonescc879a92021-11-18 22:40:18 +0000769 copylinkfile_path = os.path.join(self.manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +0800770 old_copylinkfile_paths = {}
771
772 if os.path.exists(copylinkfile_path):
773 with open(copylinkfile_path, 'rb') as fp:
774 try:
775 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800776 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +0800777 print('error: %s is not a json formatted file.' %
778 copylinkfile_path, file=sys.stderr)
779 platform_utils.remove(copylinkfile_path)
780 return False
781
782 need_remove_files = []
783 need_remove_files.extend(
784 set(old_copylinkfile_paths.get('linkfile', [])) -
785 set(new_linkfile_paths))
786 need_remove_files.extend(
787 set(old_copylinkfile_paths.get('copyfile', [])) -
788 set(new_copyfile_paths))
789
790 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -0400791 # Try to remove the updated copyfile or linkfile.
792 # So, if the file is not exist, nothing need to do.
793 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +0800794
795 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
796 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
797 json.dump(new_paths, fp)
798 return True
799
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400800 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
801 if not self.manifest.manifest_server:
802 print('error: cannot smart sync: no manifest server defined in '
803 'manifest', file=sys.stderr)
804 sys.exit(1)
805
806 manifest_server = self.manifest.manifest_server
807 if not opt.quiet:
808 print('Using manifest server %s' % manifest_server)
809
David Pursehouseeeff3532020-02-12 11:24:10 +0900810 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400811 username = None
812 password = None
813 if opt.manifest_server_username and opt.manifest_server_password:
814 username = opt.manifest_server_username
815 password = opt.manifest_server_password
816 else:
817 try:
818 info = netrc.netrc()
819 except IOError:
820 # .netrc file does not exist or could not be opened
821 pass
822 else:
823 try:
824 parse_result = urllib.parse.urlparse(manifest_server)
825 if parse_result.hostname:
826 auth = info.authenticators(parse_result.hostname)
827 if auth:
828 username, _account, password = auth
829 else:
830 print('No credentials found for %s in .netrc'
831 % parse_result.hostname, file=sys.stderr)
832 except netrc.NetrcParseError as e:
833 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
834
835 if (username and password):
836 manifest_server = manifest_server.replace('://', '://%s:%s@' %
837 (username, password),
838 1)
839
840 transport = PersistentTransport(manifest_server)
841 if manifest_server.startswith('persistent-'):
842 manifest_server = manifest_server[len('persistent-'):]
843
844 try:
845 server = xmlrpc.client.Server(manifest_server, transport=transport)
846 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800847 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400848
Mike Frysinger56ce3462019-12-04 19:30:48 -0500849 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500850 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400851 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500852 elif ('TARGET_PRODUCT' in os.environ and
853 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500854 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
855 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400856 [success, manifest_str] = server.GetApprovedManifest(branch, target)
857 else:
858 [success, manifest_str] = server.GetApprovedManifest(branch)
859 else:
860 assert(opt.smart_tag)
861 [success, manifest_str] = server.GetManifest(opt.smart_tag)
862
863 if success:
864 manifest_name = os.path.basename(smart_sync_manifest_path)
865 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500866 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400867 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400868 except IOError as e:
869 print('error: cannot write manifest to %s:\n%s'
870 % (smart_sync_manifest_path, e),
871 file=sys.stderr)
872 sys.exit(1)
873 self._ReloadManifest(manifest_name)
874 else:
875 print('error: manifest server RPC call failed: %s' %
876 manifest_str, file=sys.stderr)
877 sys.exit(1)
878 except (socket.error, IOError, xmlrpc.client.Fault) as e:
879 print('error: cannot connect to manifest server %s:\n%s'
880 % (self.manifest.manifest_server, e), file=sys.stderr)
881 sys.exit(1)
882 except xmlrpc.client.ProtocolError as e:
883 print('error: cannot connect to manifest server %s:\n%d %s'
884 % (self.manifest.manifest_server, e.errcode, e.errmsg),
885 file=sys.stderr)
886 sys.exit(1)
887
888 return manifest_name
889
Mike Frysingerfb527e32019-08-27 02:34:32 -0400890 def _UpdateManifestProject(self, opt, mp, manifest_name):
891 """Fetch & update the local manifest project."""
892 if not opt.local_only:
893 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500894 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700895 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200896 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500897 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400898 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600899 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400900 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700901 clone_filter=self.manifest.CloneFilter,
902 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400903 finish = time.time()
904 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
905 start, finish, success)
906
907 if mp.HasChanges:
908 syncbuf = SyncBuffer(mp.config)
909 start = time.time()
910 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
911 clean = syncbuf.Finish()
912 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
913 start, time.time(), clean)
914 if not clean:
915 sys.exit(1)
Mike Frysinger05638bf2021-05-04 15:33:31 -0400916 self._ReloadManifest(manifest_name)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400917 if opt.jobs is None:
918 self.jobs = self.manifest.default.sync_j
919
Mike Frysingerae6cb082019-08-27 01:10:59 -0400920 def ValidateOptions(self, opt, args):
921 if opt.force_broken:
922 print('warning: -f/--force-broken is now the default behavior, and the '
923 'options are deprecated', file=sys.stderr)
924 if opt.network_only and opt.detach_head:
925 self.OptionParser.error('cannot combine -n and -d')
926 if opt.network_only and opt.local_only:
927 self.OptionParser.error('cannot combine -n and -l')
928 if opt.manifest_name and opt.smart_sync:
929 self.OptionParser.error('cannot combine -m and -s')
930 if opt.manifest_name and opt.smart_tag:
931 self.OptionParser.error('cannot combine -m and -t')
932 if opt.manifest_server_username or opt.manifest_server_password:
933 if not (opt.smart_sync or opt.smart_tag):
934 self.OptionParser.error('-u and -p may only be combined with -s or -t')
935 if None in [opt.manifest_server_username, opt.manifest_server_password]:
936 self.OptionParser.error('both -u and -p must be given')
937
Mike Frysinger0531a622021-11-05 15:22:01 -0400938 if opt.prune is None:
939 opt.prune = True
940
LaMont Jonescc879a92021-11-18 22:40:18 +0000941 if self.manifest.is_multimanifest and not opt.this_manifest_only and args:
942 self.OptionParser.error('partial syncs must use --this-manifest-only')
943
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700944 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800945 if opt.jobs:
946 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700947 if self.jobs > 1:
948 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400949 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700950
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500951 if opt.manifest_name:
952 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700953
Chirayu Desaia892b102013-06-11 14:18:46 +0530954 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900955 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900956 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530957
Xin Lid79a4bc2020-05-20 16:03:45 -0700958 if opt.clone_bundle is None:
959 opt.clone_bundle = self.manifest.CloneBundle
960
Victor Boivie08c880d2011-04-19 10:32:52 +0200961 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400962 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
963 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900964 if os.path.isfile(smart_sync_manifest_path):
965 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800966 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900967 except OSError as e:
968 print('error: failed to remove existing smart sync override manifest: %s' %
969 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700970
Mike Frysingerc99322a2021-05-04 15:32:43 -0400971 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400972
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700973 rp = self.manifest.repoProject
974 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500975 cb = rp.CurrentBranch
976 if cb:
977 base = rp.GetBranch(cb).merge
978 if not base or not base.startswith('refs/heads/'):
979 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400980 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500981 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700982
983 mp = self.manifest.manifestProject
LaMont Jonesd82be3e2022-04-05 19:30:46 +0000984 is_standalone_manifest = bool(mp.standalone_manifest_url)
Jack Neus03ff2762021-10-15 15:43:19 +0000985 if not is_standalone_manifest:
986 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700987
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800988 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700989 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800990
Fredrik de Grootcc960972019-11-22 09:04:31 +0100991 if not opt.mp_update:
992 print('Skipping update of local manifest project.')
Jack Neus03ff2762021-10-15 15:43:19 +0000993 elif not is_standalone_manifest:
Fredrik de Grootcc960972019-11-22 09:04:31 +0100994 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700995
Raman Tennetifeb28912021-05-02 19:47:29 -0700996 load_local_manifests = not self.manifest.HasLocalManifests
LaMont Jones5fa912b2022-04-14 14:41:13 +0000997 use_superproject = git_superproject.UseSuperproject(opt.use_superproject,
998 self.manifest)
Raman Tenneticf0ba482021-12-06 18:12:59 -0800999 if use_superproject and (self.manifest.IsMirror or self.manifest.IsArchive):
Raman Tenneti6bd89aa2021-11-16 11:48:09 -08001000 # Don't use superproject, because we have no working tree.
1001 use_superproject = False
Raman Tenneticf0ba482021-12-06 18:12:59 -08001002 if opt.use_superproject is not None:
1003 print('Defaulting to no-use-superproject because there is no working tree.')
Raman Tenneti7954de12021-07-28 14:36:49 -07001004 superproject_logging_data = {
1005 'superproject': use_superproject,
1006 'haslocalmanifests': bool(self.manifest.HasLocalManifests),
Raman Tennetib55769a2021-08-13 11:47:24 -07001007 'hassuperprojecttag': bool(self.manifest.superproject),
Raman Tenneti7954de12021-07-28 14:36:49 -07001008 }
1009 if use_superproject:
1010 manifest_name = self._UpdateProjectsRevisionId(
1011 opt, args, load_local_manifests, superproject_logging_data) or opt.manifest_name
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001012
Simran Basib9a1b732015-08-20 12:19:28 -07001013 if self.gitc_manifest:
1014 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -07001015 missing_ok=True)
1016 gitc_projects = []
1017 opened_projects = []
1018 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001019 if project.relpath in self.gitc_manifest.paths and \
1020 self.gitc_manifest.paths[project.relpath].old_revision:
1021 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001022 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001023 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001024
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001025 if not args:
1026 gitc_projects = None
1027
1028 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001029 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001030 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1031 if manifest_name:
1032 manifest.Override(manifest_name)
1033 else:
1034 manifest.Override(self.manifest.manifestFile)
1035 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1036 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001037 gitc_projects)
1038 print('GITC client successfully synced.')
1039
1040 # The opened projects need to be synced as normal, therefore we
1041 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001042 # TODO: make this more reliable -- if there's a project name/path overlap,
1043 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +09001044 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
1045 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001046 if not args:
1047 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001048 all_projects = self.GetProjects(args,
1049 missing_ok=True,
1050 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001051
Mike Frysinger5a033082019-09-23 19:21:20 -04001052 err_network_sync = False
1053 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001054
Dave Borowitz67700e92012-10-23 15:00:54 -07001055 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001056 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001057 with multiprocessing.Manager() as manager:
1058 with ssh.ProxyManager(manager) as ssh_proxy:
1059 # Initialize the socket dir once in the parent.
1060 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001061 all_projects = self._FetchMain(opt, args, all_projects, err_event,
1062 manifest_name, load_local_manifests,
1063 ssh_proxy)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001064
1065 if opt.network_only:
1066 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001067
1068 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001069 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001070 err_network_sync = True
1071 if opt.fail_fast:
1072 print('\nerror: Exited sync due to fetch errors.\n'
1073 'Local checkouts *not* updated. Resolve network issues & '
1074 'retry.\n'
1075 '`repo sync -l` will update some local checkouts.',
1076 file=sys.stderr)
1077 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001078
Julien Campergue335f5ef2013-10-16 11:02:35 +02001079 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001080 # bail out now, we have no working tree
1081 return
1082
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001083 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001084 err_event.set()
1085 err_update_projects = True
1086 if opt.fail_fast:
1087 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1088 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001089
Mike Frysinger14208f42021-05-04 15:31:51 -04001090 err_update_linkfiles = not self.UpdateCopyLinkfileList()
1091 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001092 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001093 if opt.fail_fast:
1094 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1095 sys.exit(1)
1096
Mike Frysinger5a033082019-09-23 19:21:20 -04001097 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001098 # NB: We don't exit here because this is the last step.
1099 err_checkout = not self._Checkout(all_projects, opt, err_results)
1100 if err_checkout:
1101 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001102
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001103 # If there's a notice that's supposed to print at the end of the sync, print
1104 # it now...
1105 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001106 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001107
Mike Frysinger5a033082019-09-23 19:21:20 -04001108 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001109 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001110 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1111 if err_network_sync:
1112 print('error: Downloading network changes failed.', file=sys.stderr)
1113 if err_update_projects:
1114 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001115 if err_update_linkfiles:
1116 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001117 if err_checkout:
1118 print('error: Checking out local projects failed.', file=sys.stderr)
1119 if err_results:
1120 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1121 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1122 file=sys.stderr)
1123 sys.exit(1)
1124
Raman Tenneti7954de12021-07-28 14:36:49 -07001125 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001126 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1127 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001128
1129 # Update and log with the new sync analysis state.
1130 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001131 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1132 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001133
Mike Frysingere19d9e12020-02-12 11:23:32 -05001134 if not opt.quiet:
1135 print('repo sync has finished successfully.')
1136
David Pursehouse819827a2020-02-12 15:20:19 +09001137
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001138def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001139 # Link the docs for the internal .repo/ layout for people
1140 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1141 if not platform_utils.islink(link):
1142 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1143 try:
1144 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001145 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001146 pass
1147
Conley Owens094cdbe2014-01-30 15:09:59 -08001148 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001149 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001150 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001151 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001152 if project.Exists:
1153 project.PostRepoUpgrade()
1154
David Pursehouse819827a2020-02-12 15:20:19 +09001155
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001156def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001157 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001158 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001159 wrapper = Wrapper()
1160 try:
1161 rev = rp.bare_git.describe(rp.GetRevisionId())
1162 except GitError:
1163 rev = None
1164 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1165 # See if we're held back due to missing signed tag.
1166 current_revid = rp.bare_git.rev_parse('HEAD')
1167 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1168 if current_revid != new_revid:
1169 # We want to switch to the new rev, but also not trash any uncommitted
1170 # changes. This helps with local testing/hacking.
1171 # If a local change has been made, we will throw that away.
1172 # We also have to make sure this will switch to an older commit if that's
1173 # the latest tag in order to support release rollback.
1174 try:
1175 rp.work_git.reset('--keep', new_rev)
1176 except GitError as e:
1177 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001178 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001179 raise RepoChangedException(['--repo-upgraded'])
1180 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001181 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001182 else:
1183 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001184 print('repo version %s is current' % rp.work_git.describe(HEAD),
1185 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001186
David Pursehouse819827a2020-02-12 15:20:19 +09001187
Dave Borowitz67700e92012-10-23 15:00:54 -07001188class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001189 _ALPHA = 0.5
1190
Dave Borowitz67700e92012-10-23 15:00:54 -07001191 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001192 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001193 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001194 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001195
1196 def Get(self, project):
1197 self._Load()
1198 return self._times.get(project.name, _ONE_DAY_S)
1199
1200 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001201 self._Load()
1202 name = project.name
1203 old = self._times.get(name, t)
1204 self._seen.add(name)
1205 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001206 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001207
1208 def _Load(self):
1209 if self._times is None:
1210 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001211 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001212 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001213 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001214 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001215 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001216
1217 def Save(self):
1218 if self._times is None:
1219 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001220
1221 to_delete = []
1222 for name in self._times:
1223 if name not in self._seen:
1224 to_delete.append(name)
1225 for name in to_delete:
1226 del self._times[name]
1227
Dave Borowitz67700e92012-10-23 15:00:54 -07001228 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001229 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001230 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001231 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001232 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001233
1234# This is a replacement for xmlrpc.client.Transport using urllib2
1235# and supporting persistent-http[s]. It cannot change hosts from
1236# request to request like the normal transport, the real url
1237# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001238
1239
Dan Willemsen0745bb22015-08-17 13:41:45 -07001240class PersistentTransport(xmlrpc.client.Transport):
1241 def __init__(self, orig_host):
1242 self.orig_host = orig_host
1243
1244 def request(self, host, handler, request_body, verbose=False):
1245 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1246 # Python doesn't understand cookies with the #HttpOnly_ prefix
1247 # Since we're only using them for HTTP, copy the file temporarily,
1248 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001249 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001250 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001251 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001252 try:
1253 with open(cookiefile) as f:
1254 for line in f:
1255 if line.startswith("#HttpOnly_"):
1256 line = line[len("#HttpOnly_"):]
1257 tmpcookiefile.write(line)
1258 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001259
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001260 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001261 try:
1262 cookiejar.load()
1263 except cookielib.LoadError:
1264 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001265 finally:
1266 tmpcookiefile.close()
1267 else:
1268 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001269
1270 proxyhandler = urllib.request.ProxyHandler
1271 if proxy:
1272 proxyhandler = urllib.request.ProxyHandler({
1273 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001274 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001275
1276 opener = urllib.request.build_opener(
1277 urllib.request.HTTPCookieProcessor(cookiejar),
1278 proxyhandler)
1279
1280 url = urllib.parse.urljoin(self.orig_host, handler)
1281 parse_results = urllib.parse.urlparse(url)
1282
1283 scheme = parse_results.scheme
1284 if scheme == 'persistent-http':
1285 scheme = 'http'
1286 if scheme == 'persistent-https':
1287 # If we're proxying through persistent-https, use http. The
1288 # proxy itself will do the https.
1289 if proxy:
1290 scheme = 'http'
1291 else:
1292 scheme = 'https'
1293
1294 # Parse out any authentication information using the base class
1295 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1296
1297 url = urllib.parse.urlunparse((
1298 scheme,
1299 host,
1300 parse_results.path,
1301 parse_results.params,
1302 parse_results.query,
1303 parse_results.fragment))
1304
1305 request = urllib.request.Request(url, request_body)
1306 if extra_headers is not None:
1307 for (name, header) in extra_headers:
1308 request.add_header(name, header)
1309 request.add_header('Content-Type', 'text/xml')
1310 try:
1311 response = opener.open(request)
1312 except urllib.error.HTTPError as e:
1313 if e.code == 501:
1314 # We may have been redirected through a login process
1315 # but our POST turned into a GET. Retry.
1316 response = opener.open(request)
1317 else:
1318 raise
1319
1320 p, u = xmlrpc.client.getparser()
1321 while 1:
1322 data = response.read(1024)
1323 if not data:
1324 break
1325 p.feed(data)
1326 p.close()
1327 return u.close()
1328
1329 def close(self):
1330 pass