blob: 4d0a5ec6d5dfd6e086db863e250716f1d158eeec [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 """
289 return git_superproject.UseSuperproject(opt, 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)
Raman Tennetib55769a2021-08-13 11:47:24 -0700309 print_messages = git_superproject.PrintMessages(opt, self.manifest)
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000310 superproject.SetPrintMessages(print_messages)
Raman Tennetiae86a462021-07-27 08:54:59 -0700311 if opt.local_only:
312 manifest_path = superproject.manifest_path
313 if manifest_path:
314 self._ReloadManifest(manifest_path, load_local_manifests)
315 return manifest_path
316
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800317 all_projects = self.GetProjects(args,
318 missing_ok=True,
319 submodules_ok=opt.fetch_submodules)
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000320 update_result = superproject.UpdateProjectsRevisionId(
321 all_projects, git_event_log=self.git_event_log)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700322 manifest_path = update_result.manifest_path
Raman Tenneti7954de12021-07-28 14:36:49 -0700323 superproject_logging_data['updatedrevisionid'] = bool(manifest_path)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700324 if manifest_path:
325 self._ReloadManifest(manifest_path, load_local_manifests)
326 else:
Raman Tennetib55769a2021-08-13 11:47:24 -0700327 if print_messages:
328 print('warning: Update of revisionId from superproject has failed, '
329 'repo sync will not use superproject to fetch the source. ',
330 'Please resync with the --no-use-superproject option to avoid this repo warning.',
331 file=sys.stderr)
Raman Tenneti8db30d62021-07-06 21:30:06 -0700332 if update_result.fatal and opt.use_superproject is not None:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700333 sys.exit(1)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800334 return manifest_path
335
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500336 def _FetchProjectList(self, opt, projects):
337 """Main function of the fetch worker.
338
339 The projects we're given share the same underlying git object store, so we
340 have to fetch them in serial.
Roy Lee18afd7f2010-05-09 04:32:08 +0800341
David James8d201162013-10-11 17:03:19 -0700342 Delegates most of the work to _FetchHelper.
343
344 Args:
345 opt: Program options returned from optparse. See _Options().
346 projects: Projects to fetch.
David James8d201162013-10-11 17:03:19 -0700347 """
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500348 return [self._FetchOne(opt, x) for x in projects]
David James8d201162013-10-11 17:03:19 -0700349
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500350 def _FetchOne(self, opt, project):
David James8d201162013-10-11 17:03:19 -0700351 """Fetch git objects for a single project.
352
David Pursehousec1b86a22012-11-14 11:36:51 +0900353 Args:
354 opt: Program options returned from optparse. See _Options().
355 project: Project object for the project to fetch.
David James8d201162013-10-11 17:03:19 -0700356
357 Returns:
358 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900359 """
David Rileye0684ad2017-04-05 00:02:59 -0700360 start = time.time()
361 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500362 buf = io.StringIO()
David Pursehousec1b86a22012-11-14 11:36:51 +0900363 try:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500364 success = project.Sync_NetworkHalf(
365 quiet=opt.quiet,
366 verbose=opt.verbose,
367 output_redir=buf,
368 current_branch_only=self._GetCurrentBranchOnly(opt),
369 force_sync=opt.force_sync,
370 clone_bundle=opt.clone_bundle,
371 tags=opt.tags, archive=self.manifest.IsArchive,
372 optimized_fetch=opt.optimized_fetch,
373 retry_fetches=opt.retry_fetches,
374 prune=opt.prune,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400375 ssh_proxy=self.ssh_proxy,
Raman Tennetif32f2432021-04-12 20:57:25 -0700376 clone_filter=self.manifest.CloneFilter,
377 partial_clone_exclude=self.manifest.PartialCloneExclude)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700378
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500379 output = buf.getvalue()
Mike Frysinger58929732021-07-02 00:29:35 -0400380 if (opt.verbose or not success) and output:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500381 print('\n' + output.rstrip())
Doug Andersonfc06ced2011-03-16 15:49:18 -0700382
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500383 if not success:
384 print('error: Cannot fetch %s from %s'
385 % (project.name, project.remote.url),
386 file=sys.stderr)
Raman Tennetiad8aa692021-04-15 09:20:51 -0700387 except GitError as e:
388 print('error.GitError: Cannot fetch %s' % str(e), file=sys.stderr)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500389 except Exception as e:
390 print('error: Cannot fetch %s (%s: %s)'
391 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
392 raise
Mike Frysinger7b586f22021-02-23 18:38:39 -0500393
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500394 finish = time.time()
395 return (success, project, start, finish)
David James8d201162013-10-11 17:03:19 -0700396
Mike Frysinger339f2df2021-05-06 00:44:42 -0400397 @classmethod
398 def _FetchInitChild(cls, ssh_proxy):
399 cls.ssh_proxy = ssh_proxy
400
401 def _Fetch(self, projects, opt, err_event, ssh_proxy):
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500402 ret = True
403
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400404 jobs = opt.jobs_network if opt.jobs_network else self.jobs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700405 fetched = set()
Mike Frysinger151701e2021-04-13 15:07:21 -0400406 pm = Progress('Fetching', len(projects), delay=False, quiet=opt.quiet)
Roy Lee18afd7f2010-05-09 04:32:08 +0800407
David James89ece422014-01-09 18:51:58 -0800408 objdir_project_map = dict()
409 for project in projects:
410 objdir_project_map.setdefault(project.objdir, []).append(project)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500411 projects_list = list(objdir_project_map.values())
David James8d201162013-10-11 17:03:19 -0700412
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500413 def _ProcessResults(results_sets):
414 ret = True
415 for results in results_sets:
416 for (success, project, start, finish) in results:
417 self._fetch_times.Set(project, finish - start)
418 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
419 start, finish, success)
420 # Check for any errors before running any more tasks.
421 # ...we'll let existing jobs finish, though.
422 if not success:
423 ret = False
424 else:
425 fetched.add(project.gitdir)
426 pm.update(msg=project.name)
427 if not ret and opt.fail_fast:
428 break
429 return ret
Doug Andersonfc06ced2011-03-16 15:49:18 -0700430
Mike Frysinger339f2df2021-05-06 00:44:42 -0400431 # We pass the ssh proxy settings via the class. This allows multiprocessing
432 # to pickle it up when spawning children. We can't pass it as an argument
433 # to _FetchProjectList below as multiprocessing is unable to pickle those.
434 Sync.ssh_proxy = None
435
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500436 # NB: Multiprocessing is heavy, so don't spin it up for one job.
Mike Frysinger49de8ef2021-04-09 00:21:02 -0400437 if len(projects_list) == 1 or jobs == 1:
Mike Frysinger339f2df2021-05-06 00:44:42 -0400438 self._FetchInitChild(ssh_proxy)
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500439 if not _ProcessResults(self._FetchProjectList(opt, x) for x in projects_list):
440 ret = False
441 else:
442 # Favor throughput over responsiveness when quiet. It seems that imap()
443 # will yield results in batches relative to chunksize, so even as the
444 # children finish a sync, we won't see the result until one child finishes
445 # ~chunksize jobs. When using a large --jobs with large chunksize, this
446 # can be jarring as there will be a large initial delay where repo looks
447 # like it isn't doing anything and sits at 0%, but then suddenly completes
448 # a lot of jobs all at once. Since this code is more network bound, we
449 # can accept a bit more CPU overhead with a smaller chunksize so that the
450 # user sees more immediate & continuous feedback.
451 if opt.quiet:
452 chunksize = WORKER_BATCH_SIZE
David James89ece422014-01-09 18:51:58 -0800453 else:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500454 pm.update(inc=0, msg='warming up')
455 chunksize = 4
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800456 with multiprocessing.Pool(jobs, initializer=self._FetchInitChild,
457 initargs=(ssh_proxy,)) as pool:
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500458 results = pool.imap_unordered(
459 functools.partial(self._FetchProjectList, opt),
460 projects_list,
461 chunksize=chunksize)
462 if not _ProcessResults(results):
463 ret = False
464 pool.close()
Roy Lee18afd7f2010-05-09 04:32:08 +0800465
Mike Frysinger339f2df2021-05-06 00:44:42 -0400466 # Cleanup the reference now that we're done with it, and we're going to
467 # release any resources it points to. If we don't, later multiprocessing
468 # usage (e.g. checkouts) will try to pickle and then crash.
469 del Sync.ssh_proxy
470
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700471 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700472 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700473
Julien Campergue335f5ef2013-10-16 11:02:35 +0200474 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400475 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200476
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500477 return (ret, fetched)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700478
Mike Frysingerb4429432021-05-05 20:03:26 -0400479 def _FetchMain(self, opt, args, all_projects, err_event, manifest_name,
Mike Frysinger339f2df2021-05-06 00:44:42 -0400480 load_local_manifests, ssh_proxy):
Mike Frysingerb4429432021-05-05 20:03:26 -0400481 """The main network fetch loop.
482
483 Args:
484 opt: Program options returned from optparse. See _Options().
485 args: Command line args used to filter out projects.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200486 all_projects: List of all projects that should be fetched.
Mike Frysingerb4429432021-05-05 20:03:26 -0400487 err_event: Whether an error was hit while processing.
488 manifest_name: Manifest file to be reloaded.
489 load_local_manifests: Whether to load local manifests.
Mike Frysinger339f2df2021-05-06 00:44:42 -0400490 ssh_proxy: SSH manager for clients & masters.
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200491
492 Returns:
493 List of all projects that should be checked out.
Mike Frysingerb4429432021-05-05 20:03:26 -0400494 """
495 rp = self.manifest.repoProject
496
497 to_fetch = []
498 now = time.time()
499 if _ONE_DAY_S <= (now - rp.LastFetch):
500 to_fetch.append(rp)
501 to_fetch.extend(all_projects)
502 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
503
Mike Frysinger339f2df2021-05-06 00:44:42 -0400504 success, fetched = self._Fetch(to_fetch, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400505 if not success:
506 err_event.set()
507
508 _PostRepoFetch(rp, opt.repo_verify)
509 if opt.network_only:
510 # bail out now; the rest touches the working tree
511 if err_event.is_set():
512 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
513 sys.exit(1)
514 return
515
516 # Iteratively fetch missing and/or nested unregistered submodules
517 previously_missing_set = set()
518 while True:
519 self._ReloadManifest(manifest_name, load_local_manifests)
520 all_projects = self.GetProjects(args,
521 missing_ok=True,
522 submodules_ok=opt.fetch_submodules)
523 missing = []
524 for project in all_projects:
525 if project.gitdir not in fetched:
526 missing.append(project)
527 if not missing:
528 break
529 # Stop us from non-stopped fetching actually-missing repos: If set of
530 # missing repos has not been changed from last fetch, we break.
531 missing_set = set(p.name for p in missing)
532 if previously_missing_set == missing_set:
533 break
534 previously_missing_set = missing_set
Mike Frysinger339f2df2021-05-06 00:44:42 -0400535 success, new_fetched = self._Fetch(missing, opt, err_event, ssh_proxy)
Mike Frysingerb4429432021-05-05 20:03:26 -0400536 if not success:
537 err_event.set()
538 fetched.update(new_fetched)
539
Peter Kjellerstedtd1776092021-05-19 19:37:23 +0200540 return all_projects
541
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500542 def _CheckoutOne(self, detach_head, force_sync, project):
Xin Li745be2e2019-06-03 11:24:30 -0700543 """Checkout work tree for one project
544
545 Args:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500546 detach_head: Whether to leave a detached HEAD.
547 force_sync: Force checking out of the repo.
Xin Li745be2e2019-06-03 11:24:30 -0700548 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700549
550 Returns:
551 Whether the fetch was successful.
552 """
Xin Li745be2e2019-06-03 11:24:30 -0700553 start = time.time()
554 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500555 detach_head=detach_head)
Xin Li745be2e2019-06-03 11:24:30 -0700556 success = False
557 try:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500558 project.Sync_LocalHalf(syncbuf, force_sync=force_sync)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500559 success = syncbuf.Finish()
Raman Tennetiad8aa692021-04-15 09:20:51 -0700560 except GitError as e:
561 print('error.GitError: Cannot checkout %s: %s' %
562 (project.name, str(e)), file=sys.stderr)
Mike Frysingerebf04a42021-02-23 20:48:04 -0500563 except Exception as e:
564 print('error: Cannot checkout %s: %s: %s' %
565 (project.name, type(e).__name__, str(e)),
566 file=sys.stderr)
567 raise
Xin Li745be2e2019-06-03 11:24:30 -0700568
Mike Frysingerebf04a42021-02-23 20:48:04 -0500569 if not success:
570 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
571 finish = time.time()
572 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700573
Mike Frysingerebf04a42021-02-23 20:48:04 -0500574 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700575 """Checkout projects listed in all_projects
576
577 Args:
578 all_projects: List of all projects that should be checked out.
579 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500580 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700581 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500582 # Only checkout projects with worktrees.
583 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700584
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500585 def _ProcessResults(pool, pm, results):
586 ret = True
Mike Frysingerebf04a42021-02-23 20:48:04 -0500587 for (success, project, start, finish) in results:
588 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
589 start, finish, success)
590 # Check for any errors before running any more tasks.
Mike Frysingerb2fa30a2021-02-24 00:15:32 -0500591 # ...we'll let existing jobs finish, though.
Mike Frysingerebf04a42021-02-23 20:48:04 -0500592 if not success:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500593 ret = False
Mike Frysingerebf04a42021-02-23 20:48:04 -0500594 err_results.append(project.relpath)
595 if opt.fail_fast:
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500596 if pool:
597 pool.close()
598 return ret
Mike Frysingerebf04a42021-02-23 20:48:04 -0500599 pm.update(msg=project.name)
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500600 return ret
Xin Li745be2e2019-06-03 11:24:30 -0700601
Mike Frysingerb5d075d2021-03-01 00:56:38 -0500602 return self.ExecuteInParallel(
603 opt.jobs_checkout if opt.jobs_checkout else self.jobs,
604 functools.partial(self._CheckoutOne, opt.detach_head, opt.force_sync),
605 all_projects,
606 callback=_ProcessResults,
607 output=Progress('Checking out', len(all_projects), quiet=opt.quiet)) and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500608
Mike Frysinger5a033082019-09-23 19:21:20 -0400609 def _GCProjects(self, projects, opt, err_event):
Mike Frysinger151701e2021-04-13 15:07:21 -0400610 pm = Progress('Garbage collecting', len(projects), delay=False, quiet=opt.quiet)
Mike Frysinger65af2602021-04-08 22:47:44 -0400611 pm.update(inc=0, msg='prescan')
612
Allen Webb4ee4a452021-10-07 10:42:38 -0500613 tidy_dirs = {}
David James8d201162013-10-11 17:03:19 -0700614 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500615 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500616 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900617 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100618 if not opt.quiet:
Mike Frysinger65af2602021-04-08 22:47:44 -0400619 print('\r%s: Shared project %s found, disabling pruning.' %
Anders Björklund2a2da802021-01-18 10:32:36 +0100620 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500621 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500622 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500623 else:
624 # This isn't perfect, but it's the best we can do with old git.
Mike Frysinger65af2602021-04-08 22:47:44 -0400625 print('\r%s: WARNING: shared projects are unreliable when using old '
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500626 'versions of git; please upgrade to git-2.7.0+.'
627 % (project.relpath,),
628 file=sys.stderr)
629 project.config.SetString('gc.pruneExpire', 'never')
Allen Webb669efd02021-10-01 15:25:31 -0500630 project.config.SetString('gc.autoDetach', 'false')
Allen Webb4ee4a452021-10-07 10:42:38 -0500631 # Only call git gc once per objdir, but call pack-refs for the remainder.
632 if project.objdir not in tidy_dirs:
633 tidy_dirs[project.objdir] = (
634 True, # Run a full gc.
635 project.bare_git,
636 )
637 elif project.gitdir not in tidy_dirs:
638 tidy_dirs[project.gitdir] = (
639 False, # Do not run a full gc; just run pack-refs.
640 project.bare_git,
641 )
Mike Frysinger65af2602021-04-08 22:47:44 -0400642
643 cpu_count = os.cpu_count()
Dave Borowitz18857212012-10-23 17:02:59 -0700644 jobs = min(self.jobs, cpu_count)
645
646 if jobs < 2:
Allen Webb4ee4a452021-10-07 10:42:38 -0500647 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysinger65af2602021-04-08 22:47:44 -0400648 pm.update(msg=bare_git._project.name)
Allen Webb4ee4a452021-10-07 10:42:38 -0500649 if run_gc:
650 bare_git.gc('--auto')
651 else:
652 bare_git.pack_refs()
Mike Frysinger65af2602021-04-08 22:47:44 -0400653 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700654 return
655
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400656 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700657
658 threads = set()
659 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700660
Allen Webb4ee4a452021-10-07 10:42:38 -0500661 def tidy_up(run_gc, bare_git):
Mike Frysinger65af2602021-04-08 22:47:44 -0400662 pm.start(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700663 try:
664 try:
Allen Webb4ee4a452021-10-07 10:42:38 -0500665 if run_gc:
666 bare_git.gc('--auto', config=config)
667 else:
668 bare_git.pack_refs(config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700669 except GitError:
670 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900671 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700672 err_event.set()
673 raise
674 finally:
Mike Frysinger65af2602021-04-08 22:47:44 -0400675 pm.finish(bare_git._project.name)
Dave Borowitz18857212012-10-23 17:02:59 -0700676 sem.release()
677
Allen Webb4ee4a452021-10-07 10:42:38 -0500678 for (run_gc, bare_git) in tidy_dirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500679 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700680 break
681 sem.acquire()
Allen Webb4ee4a452021-10-07 10:42:38 -0500682 t = _threading.Thread(target=tidy_up, args=(run_gc, bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700683 t.daemon = True
684 threads.add(t)
685 t.start()
686
687 for t in threads:
688 t.join()
Mike Frysinger65af2602021-04-08 22:47:44 -0400689 pm.end()
Dave Borowitz18857212012-10-23 17:02:59 -0700690
Raman Tennetifeb28912021-05-02 19:47:29 -0700691 def _ReloadManifest(self, manifest_name=None, load_local_manifests=True):
692 """Reload the manfiest from the file specified by the |manifest_name|.
693
694 It unloads the manifest if |manifest_name| is None.
695
696 Args:
697 manifest_name: Manifest file to be reloaded.
698 load_local_manifests: Whether to load local manifests.
699 """
Tim Kilbourn07669002013-03-08 15:02:49 -0800700 if manifest_name:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000701 # Override calls Unload already
Raman Tennetifeb28912021-05-02 19:47:29 -0700702 self.manifest.Override(manifest_name, load_local_manifests=load_local_manifests)
Tim Kilbourn07669002013-03-08 15:02:49 -0800703 else:
LaMont Jonesa2ff20d2022-04-07 16:49:06 +0000704 self.manifest.Unload()
Tim Kilbourn07669002013-03-08 15:02:49 -0800705
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500706 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700707 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700708 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700709 if project.relpath:
710 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700711 file_name = 'project.list'
LaMont Jonescc879a92021-11-18 22:40:18 +0000712 file_path = os.path.join(self.manifest.subdir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700713 old_project_paths = []
714
715 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500716 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700717 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800718 # In reversed order, so subfolders are deleted before parent folder.
719 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700720 if not path:
721 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700722 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900723 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700724 gitdir = os.path.join(self.manifest.topdir, path, '.git')
725 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900726 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900727 manifest=self.manifest,
728 name=path,
729 remote=RemoteSpec('origin'),
730 gitdir=gitdir,
731 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500732 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900733 worktree=os.path.join(self.manifest.topdir, path),
734 relpath=path,
735 revisionExpr='HEAD',
736 revisionId=None,
737 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500738 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900739 quiet=opt.quiet,
740 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400741 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700742
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700743 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500744 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700745 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700746 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700747 return 0
748
jiajia tanga590e642021-04-25 20:02:02 +0800749 def UpdateCopyLinkfileList(self):
750 """Save all dests of copyfile and linkfile, and update them if needed.
751
752 Returns:
753 Whether update was successful.
754 """
755 new_paths = {}
756 new_linkfile_paths = []
757 new_copyfile_paths = []
758 for project in self.GetProjects(None, missing_ok=True):
759 new_linkfile_paths.extend(x.dest for x in project.linkfiles)
760 new_copyfile_paths.extend(x.dest for x in project.copyfiles)
761
762 new_paths = {
763 'linkfile': new_linkfile_paths,
764 'copyfile': new_copyfile_paths,
765 }
766
767 copylinkfile_name = 'copy-link-files.json'
LaMont Jonescc879a92021-11-18 22:40:18 +0000768 copylinkfile_path = os.path.join(self.manifest.subdir, copylinkfile_name)
jiajia tanga590e642021-04-25 20:02:02 +0800769 old_copylinkfile_paths = {}
770
771 if os.path.exists(copylinkfile_path):
772 with open(copylinkfile_path, 'rb') as fp:
773 try:
774 old_copylinkfile_paths = json.load(fp)
Raman Tenneti4a478ed2021-11-17 18:38:24 -0800775 except Exception:
jiajia tanga590e642021-04-25 20:02:02 +0800776 print('error: %s is not a json formatted file.' %
777 copylinkfile_path, file=sys.stderr)
778 platform_utils.remove(copylinkfile_path)
779 return False
780
781 need_remove_files = []
782 need_remove_files.extend(
783 set(old_copylinkfile_paths.get('linkfile', [])) -
784 set(new_linkfile_paths))
785 need_remove_files.extend(
786 set(old_copylinkfile_paths.get('copyfile', [])) -
787 set(new_copyfile_paths))
788
789 for need_remove_file in need_remove_files:
Mike Frysinger9d96f582021-09-28 11:27:24 -0400790 # Try to remove the updated copyfile or linkfile.
791 # So, if the file is not exist, nothing need to do.
792 platform_utils.remove(need_remove_file, missing_ok=True)
jiajia tanga590e642021-04-25 20:02:02 +0800793
794 # Create copy-link-files.json, save dest path of "copyfile" and "linkfile".
795 with open(copylinkfile_path, 'w', encoding='utf-8') as fp:
796 json.dump(new_paths, fp)
797 return True
798
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400799 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
800 if not self.manifest.manifest_server:
801 print('error: cannot smart sync: no manifest server defined in '
802 'manifest', file=sys.stderr)
803 sys.exit(1)
804
805 manifest_server = self.manifest.manifest_server
806 if not opt.quiet:
807 print('Using manifest server %s' % manifest_server)
808
David Pursehouseeeff3532020-02-12 11:24:10 +0900809 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400810 username = None
811 password = None
812 if opt.manifest_server_username and opt.manifest_server_password:
813 username = opt.manifest_server_username
814 password = opt.manifest_server_password
815 else:
816 try:
817 info = netrc.netrc()
818 except IOError:
819 # .netrc file does not exist or could not be opened
820 pass
821 else:
822 try:
823 parse_result = urllib.parse.urlparse(manifest_server)
824 if parse_result.hostname:
825 auth = info.authenticators(parse_result.hostname)
826 if auth:
827 username, _account, password = auth
828 else:
829 print('No credentials found for %s in .netrc'
830 % parse_result.hostname, file=sys.stderr)
831 except netrc.NetrcParseError as e:
832 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
833
834 if (username and password):
835 manifest_server = manifest_server.replace('://', '://%s:%s@' %
836 (username, password),
837 1)
838
839 transport = PersistentTransport(manifest_server)
840 if manifest_server.startswith('persistent-'):
841 manifest_server = manifest_server[len('persistent-'):]
842
843 try:
844 server = xmlrpc.client.Server(manifest_server, transport=transport)
845 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800846 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400847
Mike Frysinger56ce3462019-12-04 19:30:48 -0500848 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500849 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400850 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500851 elif ('TARGET_PRODUCT' in os.environ and
852 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500853 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
854 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400855 [success, manifest_str] = server.GetApprovedManifest(branch, target)
856 else:
857 [success, manifest_str] = server.GetApprovedManifest(branch)
858 else:
859 assert(opt.smart_tag)
860 [success, manifest_str] = server.GetManifest(opt.smart_tag)
861
862 if success:
863 manifest_name = os.path.basename(smart_sync_manifest_path)
864 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500865 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400866 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400867 except IOError as e:
868 print('error: cannot write manifest to %s:\n%s'
869 % (smart_sync_manifest_path, e),
870 file=sys.stderr)
871 sys.exit(1)
872 self._ReloadManifest(manifest_name)
873 else:
874 print('error: manifest server RPC call failed: %s' %
875 manifest_str, file=sys.stderr)
876 sys.exit(1)
877 except (socket.error, IOError, xmlrpc.client.Fault) as e:
878 print('error: cannot connect to manifest server %s:\n%s'
879 % (self.manifest.manifest_server, e), file=sys.stderr)
880 sys.exit(1)
881 except xmlrpc.client.ProtocolError as e:
882 print('error: cannot connect to manifest server %s:\n%d %s'
883 % (self.manifest.manifest_server, e.errcode, e.errmsg),
884 file=sys.stderr)
885 sys.exit(1)
886
887 return manifest_name
888
Mike Frysingerfb527e32019-08-27 02:34:32 -0400889 def _UpdateManifestProject(self, opt, mp, manifest_name):
890 """Fetch & update the local manifest project."""
891 if not opt.local_only:
892 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500893 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Raman Tenneti2ae44d72021-03-23 15:12:27 -0700894 current_branch_only=self._GetCurrentBranchOnly(opt),
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200895 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500896 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400897 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600898 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400899 submodules=self.manifest.HasSubmodules,
Raman Tennetif32f2432021-04-12 20:57:25 -0700900 clone_filter=self.manifest.CloneFilter,
901 partial_clone_exclude=self.manifest.PartialCloneExclude)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400902 finish = time.time()
903 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
904 start, finish, success)
905
906 if mp.HasChanges:
907 syncbuf = SyncBuffer(mp.config)
908 start = time.time()
909 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
910 clean = syncbuf.Finish()
911 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
912 start, time.time(), clean)
913 if not clean:
914 sys.exit(1)
Mike Frysinger05638bf2021-05-04 15:33:31 -0400915 self._ReloadManifest(manifest_name)
Mike Frysingerfb527e32019-08-27 02:34:32 -0400916 if opt.jobs is None:
917 self.jobs = self.manifest.default.sync_j
918
Mike Frysingerae6cb082019-08-27 01:10:59 -0400919 def ValidateOptions(self, opt, args):
920 if opt.force_broken:
921 print('warning: -f/--force-broken is now the default behavior, and the '
922 'options are deprecated', file=sys.stderr)
923 if opt.network_only and opt.detach_head:
924 self.OptionParser.error('cannot combine -n and -d')
925 if opt.network_only and opt.local_only:
926 self.OptionParser.error('cannot combine -n and -l')
927 if opt.manifest_name and opt.smart_sync:
928 self.OptionParser.error('cannot combine -m and -s')
929 if opt.manifest_name and opt.smart_tag:
930 self.OptionParser.error('cannot combine -m and -t')
931 if opt.manifest_server_username or opt.manifest_server_password:
932 if not (opt.smart_sync or opt.smart_tag):
933 self.OptionParser.error('-u and -p may only be combined with -s or -t')
934 if None in [opt.manifest_server_username, opt.manifest_server_password]:
935 self.OptionParser.error('both -u and -p must be given')
936
Mike Frysinger0531a622021-11-05 15:22:01 -0400937 if opt.prune is None:
938 opt.prune = True
939
LaMont Jonescc879a92021-11-18 22:40:18 +0000940 if self.manifest.is_multimanifest and not opt.this_manifest_only and args:
941 self.OptionParser.error('partial syncs must use --this-manifest-only')
942
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700943 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800944 if opt.jobs:
945 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700946 if self.jobs > 1:
947 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400948 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700949
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500950 if opt.manifest_name:
951 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700952
Chirayu Desaia892b102013-06-11 14:18:46 +0530953 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900954 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900955 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530956
Xin Lid79a4bc2020-05-20 16:03:45 -0700957 if opt.clone_bundle is None:
958 opt.clone_bundle = self.manifest.CloneBundle
959
Victor Boivie08c880d2011-04-19 10:32:52 +0200960 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400961 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
962 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900963 if os.path.isfile(smart_sync_manifest_path):
964 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800965 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900966 except OSError as e:
967 print('error: failed to remove existing smart sync override manifest: %s' %
968 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700969
Mike Frysingerc99322a2021-05-04 15:32:43 -0400970 err_event = multiprocessing.Event()
Mike Frysinger5a033082019-09-23 19:21:20 -0400971
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700972 rp = self.manifest.repoProject
973 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500974 cb = rp.CurrentBranch
975 if cb:
976 base = rp.GetBranch(cb).merge
977 if not base or not base.startswith('refs/heads/'):
978 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400979 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500980 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700981
982 mp = self.manifest.manifestProject
LaMont Jonesd82be3e2022-04-05 19:30:46 +0000983 is_standalone_manifest = bool(mp.standalone_manifest_url)
Jack Neus03ff2762021-10-15 15:43:19 +0000984 if not is_standalone_manifest:
985 mp.PreSync()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700986
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800987 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700988 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800989
Fredrik de Grootcc960972019-11-22 09:04:31 +0100990 if not opt.mp_update:
991 print('Skipping update of local manifest project.')
Jack Neus03ff2762021-10-15 15:43:19 +0000992 elif not is_standalone_manifest:
Fredrik de Grootcc960972019-11-22 09:04:31 +0100993 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700994
Raman Tennetifeb28912021-05-02 19:47:29 -0700995 load_local_manifests = not self.manifest.HasLocalManifests
Raman Tenneti7954de12021-07-28 14:36:49 -0700996 use_superproject = git_superproject.UseSuperproject(opt, self.manifest)
Raman Tenneticf0ba482021-12-06 18:12:59 -0800997 if use_superproject and (self.manifest.IsMirror or self.manifest.IsArchive):
Raman Tenneti6bd89aa2021-11-16 11:48:09 -0800998 # Don't use superproject, because we have no working tree.
999 use_superproject = False
Raman Tenneticf0ba482021-12-06 18:12:59 -08001000 if opt.use_superproject is not None:
1001 print('Defaulting to no-use-superproject because there is no working tree.')
Raman Tenneti7954de12021-07-28 14:36:49 -07001002 superproject_logging_data = {
1003 'superproject': use_superproject,
1004 'haslocalmanifests': bool(self.manifest.HasLocalManifests),
Raman Tennetib55769a2021-08-13 11:47:24 -07001005 'hassuperprojecttag': bool(self.manifest.superproject),
Raman Tenneti7954de12021-07-28 14:36:49 -07001006 }
1007 if use_superproject:
1008 manifest_name = self._UpdateProjectsRevisionId(
1009 opt, args, load_local_manifests, superproject_logging_data) or opt.manifest_name
Raman Tenneti1fd7bc22021-02-04 14:39:38 -08001010
Simran Basib9a1b732015-08-20 12:19:28 -07001011 if self.gitc_manifest:
1012 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -07001013 missing_ok=True)
1014 gitc_projects = []
1015 opened_projects = []
1016 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001017 if project.relpath in self.gitc_manifest.paths and \
1018 self.gitc_manifest.paths[project.relpath].old_revision:
1019 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001020 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001021 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -07001022
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001023 if not args:
1024 gitc_projects = None
1025
1026 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -07001027 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001028 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
1029 if manifest_name:
1030 manifest.Override(manifest_name)
1031 else:
1032 manifest.Override(self.manifest.manifestFile)
1033 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
1034 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -07001035 gitc_projects)
1036 print('GITC client successfully synced.')
1037
1038 # The opened projects need to be synced as normal, therefore we
1039 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -07001040 # TODO: make this more reliable -- if there's a project name/path overlap,
1041 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +09001042 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
1043 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -07001044 if not args:
1045 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001046 all_projects = self.GetProjects(args,
1047 missing_ok=True,
1048 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001049
Mike Frysinger5a033082019-09-23 19:21:20 -04001050 err_network_sync = False
1051 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -04001052
Dave Borowitz67700e92012-10-23 15:00:54 -07001053 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -07001054 if not opt.local_only:
Mike Frysinger339f2df2021-05-06 00:44:42 -04001055 with multiprocessing.Manager() as manager:
1056 with ssh.ProxyManager(manager) as ssh_proxy:
1057 # Initialize the socket dir once in the parent.
1058 ssh_proxy.sock()
Peter Kjellerstedtd1776092021-05-19 19:37:23 +02001059 all_projects = self._FetchMain(opt, args, all_projects, err_event,
1060 manifest_name, load_local_manifests,
1061 ssh_proxy)
Mike Frysinger339f2df2021-05-06 00:44:42 -04001062
1063 if opt.network_only:
1064 return
Mike Frysinger5a033082019-09-23 19:21:20 -04001065
1066 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001067 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001068 err_network_sync = True
1069 if opt.fail_fast:
1070 print('\nerror: Exited sync due to fetch errors.\n'
1071 'Local checkouts *not* updated. Resolve network issues & '
1072 'retry.\n'
1073 '`repo sync -l` will update some local checkouts.',
1074 file=sys.stderr)
1075 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001076
Julien Campergue335f5ef2013-10-16 11:02:35 +02001077 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -07001078 # bail out now, we have no working tree
1079 return
1080
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -05001081 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -04001082 err_event.set()
1083 err_update_projects = True
1084 if opt.fail_fast:
1085 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
1086 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -07001087
Mike Frysinger14208f42021-05-04 15:31:51 -04001088 err_update_linkfiles = not self.UpdateCopyLinkfileList()
1089 if err_update_linkfiles:
jiajia tanga590e642021-04-25 20:02:02 +08001090 err_event.set()
jiajia tanga590e642021-04-25 20:02:02 +08001091 if opt.fail_fast:
1092 print('\nerror: Local update copyfile or linkfile failed.', file=sys.stderr)
1093 sys.exit(1)
1094
Mike Frysinger5a033082019-09-23 19:21:20 -04001095 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -05001096 # NB: We don't exit here because this is the last step.
1097 err_checkout = not self._Checkout(all_projects, opt, err_results)
1098 if err_checkout:
1099 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001100
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001101 # If there's a notice that's supposed to print at the end of the sync, print
1102 # it now...
1103 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001104 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -07001105
Mike Frysinger5a033082019-09-23 19:21:20 -04001106 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -05001107 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -04001108 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
1109 if err_network_sync:
1110 print('error: Downloading network changes failed.', file=sys.stderr)
1111 if err_update_projects:
1112 print('error: Updating local project lists failed.', file=sys.stderr)
jiajia tanga590e642021-04-25 20:02:02 +08001113 if err_update_linkfiles:
1114 print('error: Updating copyfiles or linkfiles failed.', file=sys.stderr)
Mike Frysinger5a033082019-09-23 19:21:20 -04001115 if err_checkout:
1116 print('error: Checking out local projects failed.', file=sys.stderr)
1117 if err_results:
1118 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
1119 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
1120 file=sys.stderr)
1121 sys.exit(1)
1122
Raman Tenneti7954de12021-07-28 14:36:49 -07001123 # Log the previous sync analysis state from the config.
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001124 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1125 'previous_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001126
1127 # Update and log with the new sync analysis state.
1128 mp.config.UpdateSyncAnalysisState(opt, superproject_logging_data)
Raman Tenneti6448a4f2021-09-13 17:40:07 -07001129 self.git_event_log.LogDataConfigEvents(mp.config.GetSyncAnalysisStateData(),
1130 'current_sync_state')
Raman Tenneti7954de12021-07-28 14:36:49 -07001131
Mike Frysingere19d9e12020-02-12 11:23:32 -05001132 if not opt.quiet:
1133 print('repo sync has finished successfully.')
1134
David Pursehouse819827a2020-02-12 15:20:19 +09001135
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001136def _PostRepoUpgrade(manifest, quiet=False):
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001137 # Link the docs for the internal .repo/ layout for people
1138 link = os.path.join(manifest.repodir, 'internal-fs-layout.md')
1139 if not platform_utils.islink(link):
1140 target = os.path.join('repo', 'docs', 'internal-fs-layout.md')
1141 try:
1142 platform_utils.symlink(target, link)
Raman Tenneti4a478ed2021-11-17 18:38:24 -08001143 except Exception:
Mike Frysingerfdeb20f2021-11-14 03:53:04 -05001144 pass
1145
Conley Owens094cdbe2014-01-30 15:09:59 -08001146 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -07001147 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -07001148 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -08001149 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001150 if project.Exists:
1151 project.PostRepoUpgrade()
1152
David Pursehouse819827a2020-02-12 15:20:19 +09001153
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001154def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001155 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001156 print('info: A new version of repo is available', file=sys.stderr)
Mike Frysinger347f9ed2021-03-15 14:58:52 -04001157 wrapper = Wrapper()
1158 try:
1159 rev = rp.bare_git.describe(rp.GetRevisionId())
1160 except GitError:
1161 rev = None
1162 _, new_rev = wrapper.check_repo_rev(rp.gitdir, rev, repo_verify=repo_verify)
1163 # See if we're held back due to missing signed tag.
1164 current_revid = rp.bare_git.rev_parse('HEAD')
1165 new_revid = rp.bare_git.rev_parse('--verify', new_rev)
1166 if current_revid != new_revid:
1167 # We want to switch to the new rev, but also not trash any uncommitted
1168 # changes. This helps with local testing/hacking.
1169 # If a local change has been made, we will throw that away.
1170 # We also have to make sure this will switch to an older commit if that's
1171 # the latest tag in order to support release rollback.
1172 try:
1173 rp.work_git.reset('--keep', new_rev)
1174 except GitError as e:
1175 sys.exit(str(e))
Sarah Owenscecd1d82012-11-01 22:59:27 -07001176 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001177 raise RepoChangedException(['--repo-upgraded'])
1178 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001179 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001180 else:
1181 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001182 print('repo version %s is current' % rp.work_git.describe(HEAD),
1183 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -07001184
David Pursehouse819827a2020-02-12 15:20:19 +09001185
Dave Borowitz67700e92012-10-23 15:00:54 -07001186class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001187 _ALPHA = 0.5
1188
Dave Borowitz67700e92012-10-23 15:00:54 -07001189 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001190 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001191 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001192 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001193
1194 def Get(self, project):
1195 self._Load()
1196 return self._times.get(project.name, _ONE_DAY_S)
1197
1198 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001199 self._Load()
1200 name = project.name
1201 old = self._times.get(name, t)
1202 self._seen.add(name)
1203 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001204 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001205
1206 def _Load(self):
1207 if self._times is None:
1208 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001209 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001210 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001211 except (IOError, ValueError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001212 platform_utils.remove(self._path, missing_ok=True)
Anthony King85b24ac2014-05-06 15:57:48 +01001213 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001214
1215 def Save(self):
1216 if self._times is None:
1217 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001218
1219 to_delete = []
1220 for name in self._times:
1221 if name not in self._seen:
1222 to_delete.append(name)
1223 for name in to_delete:
1224 del self._times[name]
1225
Dave Borowitz67700e92012-10-23 15:00:54 -07001226 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001227 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001228 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001229 except (IOError, TypeError):
Mike Frysinger9d96f582021-09-28 11:27:24 -04001230 platform_utils.remove(self._path, missing_ok=True)
Dan Willemsen0745bb22015-08-17 13:41:45 -07001231
1232# This is a replacement for xmlrpc.client.Transport using urllib2
1233# and supporting persistent-http[s]. It cannot change hosts from
1234# request to request like the normal transport, the real url
1235# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001236
1237
Dan Willemsen0745bb22015-08-17 13:41:45 -07001238class PersistentTransport(xmlrpc.client.Transport):
1239 def __init__(self, orig_host):
1240 self.orig_host = orig_host
1241
1242 def request(self, host, handler, request_body, verbose=False):
1243 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1244 # Python doesn't understand cookies with the #HttpOnly_ prefix
1245 # Since we're only using them for HTTP, copy the file temporarily,
1246 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001247 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001248 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001249 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001250 try:
1251 with open(cookiefile) as f:
1252 for line in f:
1253 if line.startswith("#HttpOnly_"):
1254 line = line[len("#HttpOnly_"):]
1255 tmpcookiefile.write(line)
1256 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001257
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001258 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001259 try:
1260 cookiejar.load()
1261 except cookielib.LoadError:
1262 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001263 finally:
1264 tmpcookiefile.close()
1265 else:
1266 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001267
1268 proxyhandler = urllib.request.ProxyHandler
1269 if proxy:
1270 proxyhandler = urllib.request.ProxyHandler({
1271 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001272 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001273
1274 opener = urllib.request.build_opener(
1275 urllib.request.HTTPCookieProcessor(cookiejar),
1276 proxyhandler)
1277
1278 url = urllib.parse.urljoin(self.orig_host, handler)
1279 parse_results = urllib.parse.urlparse(url)
1280
1281 scheme = parse_results.scheme
1282 if scheme == 'persistent-http':
1283 scheme = 'http'
1284 if scheme == 'persistent-https':
1285 # If we're proxying through persistent-https, use http. The
1286 # proxy itself will do the https.
1287 if proxy:
1288 scheme = 'http'
1289 else:
1290 scheme = 'https'
1291
1292 # Parse out any authentication information using the base class
1293 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1294
1295 url = urllib.parse.urlunparse((
1296 scheme,
1297 host,
1298 parse_results.path,
1299 parse_results.params,
1300 parse_results.query,
1301 parse_results.fragment))
1302
1303 request = urllib.request.Request(url, request_body)
1304 if extra_headers is not None:
1305 for (name, header) in extra_headers:
1306 request.add_header(name, header)
1307 request.add_header('Content-Type', 'text/xml')
1308 try:
1309 response = opener.open(request)
1310 except urllib.error.HTTPError as e:
1311 if e.code == 501:
1312 # We may have been redirected through a login process
1313 # but our POST turned into a GET. Retry.
1314 response = opener.open(request)
1315 else:
1316 raise
1317
1318 p, u = xmlrpc.client.getparser()
1319 while 1:
1320 data = response.read(1024)
1321 if not data:
1322 break
1323 p.feed(data)
1324 p.close()
1325 return u.close()
1326
1327 def close(self):
1328 pass