blob: 2953ee3bf578025e0ad89d9ed723db038ef99636 [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
23import re
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -070024import socket
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import subprocess
26import sys
Dan Willemsen0745bb22015-08-17 13:41:45 -070027import tempfile
Shawn O. Pearcef6906872009-04-18 10:49:00 -070028import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040029import urllib.error
30import urllib.parse
31import urllib.request
32import xmlrpc.client
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070033
Roy Lee18afd7f2010-05-09 04:32:08 +080034try:
35 import threading as _threading
36except ImportError:
37 import dummy_threading as _threading
38
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070039try:
40 import resource
David Pursehouse819827a2020-02-12 15:20:19 +090041
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -070042 def _rlimit_nofile():
43 return resource.getrlimit(resource.RLIMIT_NOFILE)
44except ImportError:
45 def _rlimit_nofile():
46 return (256, 256)
47
Dave Borowitz18857212012-10-23 17:02:59 -070048try:
49 import multiprocessing
50except ImportError:
51 multiprocessing = None
52
David Rileye0684ad2017-04-05 00:02:59 -070053import event_log
Dave Borowitze2152672012-10-31 12:24:38 -070054from git_command import GIT, git_require
David Pursehouseba7bc732015-08-20 16:55:42 +090055from git_config import GetUrlCookieFile
David Pursehoused94aaef2012-09-07 09:52:04 +090056from git_refs import R_HEADS, HEAD
Raman Tenneti6a872c92021-01-14 19:17:50 -080057import git_superproject
Simran Basibdb52712015-08-10 13:23:23 -070058import gitc_utils
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -070059from project import Project
60from project import RemoteSpec
Mike Frysingerebf04a42021-02-23 20:48:04 -050061from command import Command, MirrorSafeCommand, WORKER_BATCH_SIZE
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080062from error import RepoChangedException, GitError, ManifestParseError
Renaud Paquaya65adf72016-11-03 10:37:53 -070063import platform_utils
Shawn O. Pearce350cde42009-04-16 11:21:18 -070064from project import SyncBuffer
Shawn O. Pearce68194f42009-04-10 16:48:52 -070065from progress import Progress
Conley Owens094cdbe2014-01-30 15:09:59 -080066from wrapper import Wrapper
Dan Willemsen5ea32d12015-09-08 13:27:20 -070067from manifest_xml import GitcManifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070068
Dave Borowitz67700e92012-10-23 15:00:54 -070069_ONE_DAY_S = 24 * 60 * 60
70
David Pursehouse819827a2020-02-12 15:20:19 +090071
Doug Andersonfc06ced2011-03-16 15:49:18 -070072class _FetchError(Exception):
73 """Internal error thrown in _FetchHelper() when we don't want stack trace."""
Doug Andersonfc06ced2011-03-16 15:49:18 -070074
David Pursehouse819827a2020-02-12 15:20:19 +090075
Shawn O. Pearcec95583b2009-03-03 17:47:06 -080076class Sync(Command, MirrorSafeCommand):
Roy Lee18afd7f2010-05-09 04:32:08 +080077 jobs = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070078 common = True
79 helpSummary = "Update working tree to the latest revision"
80 helpUsage = """
81%prog [<project>...]
82"""
83 helpDescription = """
84The '%prog' command synchronizes local project directories
85with the remote repositories specified in the manifest. If a local
86project does not yet exist, it will clone a new local directory from
87the remote repository and set up tracking branches as specified in
88the manifest. If the local project already exists, '%prog'
89will update the remote branches and rebase any new local changes
90on top of the new remote changes.
91
92'%prog' will synchronize all projects listed at the command
93line. Projects can be specified either by name, or by a relative
94or absolute path to the project's local directory. If no projects
95are specified, '%prog' will synchronize all projects listed in
96the manifest.
Shawn O. Pearce3e768c92009-04-10 16:59:36 -070097
98The -d/--detach option can be used to switch specified projects
99back to the manifest revision. This option is especially helpful
100if the project is currently on a topic branch, but the manifest
101revision is temporarily needed.
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700102
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700103The -s/--smart-sync option can be used to sync to a known good
104build as specified by the manifest-server element in the current
Victor Boivie08c880d2011-04-19 10:32:52 +0200105manifest. The -t/--smart-tag option is similar and allows you to
106specify a custom tag/label.
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700107
David Pursehousecf76b1b2012-09-14 10:31:42 +0900108The -u/--manifest-server-username and -p/--manifest-server-password
109options can be used to specify a username and password to authenticate
110with the manifest server when using the -s or -t option.
111
112If -u and -p are not specified when using the -s or -t option, '%prog'
113will attempt to read authentication credentials for the manifest server
114from the user's .netrc file.
115
116'%prog' will not use authentication credentials from -u/-p or .netrc
117if the manifest server specified in the manifest file already includes
118credentials.
119
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400120By default, all projects will be synced. The --fail-fast option can be used
Mike Frysinger7ae210a2020-05-24 14:56:52 -0400121to halt syncing as soon as possible when the first project fails to sync.
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500122
Kevin Degiabaa7f32014-11-12 11:27:45 -0700123The --force-sync option can be used to overwrite existing git
124directories if they have previously been linked to a different
Roger Shimizuac29ac32020-06-06 02:33:40 +0900125object directory. WARNING: This may cause data to be lost since
Kevin Degiabaa7f32014-11-12 11:27:45 -0700126refs may be removed when overwriting.
127
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500128The --force-remove-dirty option can be used to remove previously used
129projects with uncommitted changes. WARNING: This may cause data to be
130lost since uncommitted changes may be removed with projects that no longer
131exist in the manifest.
132
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700133The --no-clone-bundle option disables any attempt to use
134$URL/clone.bundle to bootstrap a new Git repository from a
135resumeable bundle file on a content delivery network. This
136may be necessary if there are problems with the local Python
137HTTP client or proxy configuration, but the Git binary works.
138
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800139The --fetch-submodules option enables fetching Git submodules
140of a project from server.
141
David Pursehousef2fad612015-01-29 14:36:28 +0900142The -c/--current-branch option can be used to only fetch objects that
143are on the branch specified by a project's revision.
144
David Pursehouseb1553542014-09-04 21:28:09 +0900145The --optimized-fetch option can be used to only fetch projects that
146are fixed to a sha1 revision if the sha1 revision does not already
147exist locally.
148
David Pursehouse74cfd272015-10-14 10:50:15 +0900149The --prune option can be used to remove any refs that no longer
150exist on the remote.
151
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400152# SSH Connections
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700153
154If at least one project remote URL uses an SSH connection (ssh://,
155git+ssh://, or user@host:path syntax) repo will automatically
156enable the SSH ControlMaster option when connecting to that host.
157This feature permits other projects in the same '%prog' session to
158reuse the same SSH tunnel, saving connection setup overheads.
159
160To disable this behavior on UNIX platforms, set the GIT_SSH
161environment variable to 'ssh'. For example:
162
163 export GIT_SSH=ssh
164 %prog
165
Mike Frysingerb8f7bb02018-10-10 01:05:11 -0400166# Compatibility
Shawn O. Pearceeb7af872009-04-21 08:02:04 -0700167
168This feature is automatically disabled on Windows, due to the lack
169of UNIX domain socket support.
170
171This feature is not compatible with url.insteadof rewrites in the
172user's ~/.gitconfig. '%prog' is currently not able to perform the
173rewrite early enough to establish the ControlMaster tunnel.
174
175If the remote SSH daemon is Gerrit Code Review, version 2.0.10 or
176later is required to fix a server side protocol bug.
177
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700178"""
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500179 PARALLEL_JOBS = 1
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700180
Nico Sallembien6623b212010-05-11 12:57:01 -0700181 def _Options(self, p, show_smart=True):
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000182 try:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500183 self.PARALLEL_JOBS = self.manifest.default.sync_j
Torne (Richard Coles)7bdbde72012-12-05 10:58:06 +0000184 except ManifestParseError:
Mike Frysinger6a2400a2021-02-16 01:43:31 -0500185 pass
186 super()._Options(p)
Shawn O. Pearce6392c872011-09-22 17:44:31 -0700187
Andrei Warkentin5df6de02010-07-02 17:58:31 -0500188 p.add_option('-f', '--force-broken',
Stefan Müller-Klieser46702ed2019-08-30 10:20:15 +0200189 dest='force_broken', action='store_true',
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400190 help='obsolete option (to be deleted in the future)')
191 p.add_option('--fail-fast',
192 dest='fail_fast', action='store_true',
193 help='stop syncing after first error is hit')
Kevin Degiabaa7f32014-11-12 11:27:45 -0700194 p.add_option('--force-sync',
195 dest='force_sync', action='store_true',
196 help="overwrite an existing git directory if it needs to "
197 "point to a different object directory. WARNING: this "
198 "may cause loss of data")
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500199 p.add_option('--force-remove-dirty',
200 dest='force_remove_dirty', action='store_true',
201 help="force remove projects with uncommitted modifications if "
202 "projects no longer exist in the manifest. "
203 "WARNING: this may cause loss of data")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900204 p.add_option('-l', '--local-only',
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700205 dest='local_only', action='store_true',
206 help="only update working tree, don't fetch")
David Pursehouse54a4e602020-02-12 14:31:05 +0900207 p.add_option('--no-manifest-update', '--nmu',
Fredrik de Grootcc960972019-11-22 09:04:31 +0100208 dest='mp_update', action='store_false', default='true',
209 help='use the existing manifest checkout as-is. '
210 '(do not update to the latest revision)')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900211 p.add_option('-n', '--network-only',
Shawn O. Pearce96fdcef2009-04-10 16:29:20 -0700212 dest='network_only', action='store_true',
213 help="fetch only, don't update working tree")
David Pursehouse8f62fb72012-11-14 12:09:38 +0900214 p.add_option('-d', '--detach',
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700215 dest='detach_head', action='store_true',
216 help='detach projects back to manifest revision')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900217 p.add_option('-c', '--current-branch',
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -0700218 dest='current_branch_only', action='store_true',
219 help='fetch only current branch from server')
Mike Frysinger521d01b2020-02-17 01:51:49 -0500220 p.add_option('-v', '--verbose',
221 dest='output_mode', action='store_true',
222 help='show all sync output')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900223 p.add_option('-q', '--quiet',
Mike Frysinger521d01b2020-02-17 01:51:49 -0500224 dest='output_mode', action='store_false',
225 help='only show errors')
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500226 p.add_option('-m', '--manifest-name',
227 dest='manifest_name',
228 help='temporary manifest to use for this sync', metavar='NAME.xml')
Xin Lid79a4bc2020-05-20 16:03:45 -0700229 p.add_option('--clone-bundle', action='store_true',
230 help='enable use of /clone.bundle on HTTP/HTTPS')
231 p.add_option('--no-clone-bundle', dest='clone_bundle', action='store_false',
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -0700232 help='disable use of /clone.bundle on HTTP/HTTPS')
Conley Owens8d070cf2012-11-06 13:14:31 -0800233 p.add_option('-u', '--manifest-server-username', action='store',
234 dest='manifest_server_username',
235 help='username to authenticate with the manifest server')
236 p.add_option('-p', '--manifest-server-password', action='store',
237 dest='manifest_server_password',
238 help='password to authenticate with the manifest server')
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800239 p.add_option('--fetch-submodules',
240 dest='fetch_submodules', action='store_true',
241 help='fetch submodules from server')
Raman Tenneti6a872c92021-01-14 19:17:50 -0800242 p.add_option('--use-superproject', action='store_true',
243 help='use the manifest superproject to sync projects')
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700244 p.add_option('--no-tags',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500245 dest='tags', default=True, action='store_false',
Mitchel Humpherys597868b2012-10-29 10:18:34 -0700246 help="don't fetch tags")
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')
David Pursehouse74cfd272015-10-14 10:50:15 +0900253 p.add_option('--prune', dest='prune', action='store_true',
254 help='delete refs that no longer exist on the remote')
Nico Sallembien6623b212010-05-11 12:57:01 -0700255 if show_smart:
256 p.add_option('-s', '--smart-sync',
257 dest='smart_sync', action='store_true',
David Pursehouse79fba682016-04-13 18:03:00 +0900258 help='smart sync using manifest from the latest known good build')
Victor Boivie08c880d2011-04-19 10:32:52 +0200259 p.add_option('-t', '--smart-tag',
260 dest='smart_tag', action='store',
261 help='smart sync using manifest from a known tag')
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700262
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700263 g = p.add_option_group('repo Version options')
264 g.add_option('--no-repo-verify',
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500265 dest='repo_verify', default=True, action='store_false',
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700266 help='do not verify repo source code')
Shawn O. Pearcefd89b672009-04-18 11:28:57 -0700267 g.add_option('--repo-upgraded',
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800268 dest='repo_upgraded', action='store_true',
Shawn O. Pearce2a1ccb22009-04-10 16:51:53 -0700269 help=SUPPRESS_HELP)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800271 def _GetBranch(self):
272 """Returns the branch name for getting the approved manifest."""
273 p = self.manifest.manifestProject
274 b = p.GetBranch(p.CurrentBranch)
275 branch = b.merge
276 if branch.startswith(R_HEADS):
277 branch = branch[len(R_HEADS):]
278 return branch
279
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800280 def _UpdateProjectsRevisionId(self, opt, args):
281 """Update revisionId of every project with the SHA from superproject.
282
283 This function updates each project's revisionId with SHA from superproject.
284 It writes the updated manifest into a file and reloads the manifest from it.
285
286 Args:
287 opt: Program options returned from optparse. See _Options().
288 args: Arguments to pass to GetProjects. See the GetProjects
289 docstring for details.
290
291 Returns:
292 Returns path to the overriding manifest file.
293 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800294 superproject = git_superproject.Superproject(self.manifest,
Raman Tennetief99ec02021-03-04 10:29:40 -0800295 self.repodir,
296 quiet=opt.quiet)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800297 all_projects = self.GetProjects(args,
298 missing_ok=True,
299 submodules_ok=opt.fetch_submodules)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800300 manifest_path = superproject.UpdateProjectsRevisionId(all_projects)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800301 if not manifest_path:
302 print('error: Update of revsionId from superproject has failed',
303 file=sys.stderr)
304 sys.exit(1)
305 self._ReloadManifest(manifest_path)
306 return manifest_path
307
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500308 def _FetchProjectList(self, opt, projects, sem, *args, **kwargs):
Xin Li745be2e2019-06-03 11:24:30 -0700309 """Main function of the fetch threads.
Roy Lee18afd7f2010-05-09 04:32:08 +0800310
David James8d201162013-10-11 17:03:19 -0700311 Delegates most of the work to _FetchHelper.
312
313 Args:
314 opt: Program options returned from optparse. See _Options().
315 projects: Projects to fetch.
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500316 sem: We'll release() this semaphore when we exit so that another thread
317 can be started up.
David James89ece422014-01-09 18:51:58 -0800318 *args, **kwargs: Remaining arguments to pass to _FetchHelper. See the
David James8d201162013-10-11 17:03:19 -0700319 _FetchHelper docstring for details.
320 """
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500321 try:
322 for project in projects:
323 success = self._FetchHelper(opt, project, *args, **kwargs)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400324 if not success and opt.fail_fast:
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500325 break
326 finally:
327 sem.release()
David James8d201162013-10-11 17:03:19 -0700328
Xin Li745be2e2019-06-03 11:24:30 -0700329 def _FetchHelper(self, opt, project, lock, fetched, pm, err_event,
330 clone_filter):
David James8d201162013-10-11 17:03:19 -0700331 """Fetch git objects for a single project.
332
David Pursehousec1b86a22012-11-14 11:36:51 +0900333 Args:
334 opt: Program options returned from optparse. See _Options().
335 project: Project object for the project to fetch.
336 lock: Lock for accessing objects that are shared amongst multiple
337 _FetchHelper() threads.
338 fetched: set object that we will add project.gitdir to when we're done
339 (with our lock held).
340 pm: Instance of a Project object. We will call pm.update() (with our
341 lock held).
David Pursehousec1b86a22012-11-14 11:36:51 +0900342 err_event: We'll set this event in the case of an error (after printing
343 out info about the error).
Xin Li745be2e2019-06-03 11:24:30 -0700344 clone_filter: Filter for use in a partial clone.
David James8d201162013-10-11 17:03:19 -0700345
346 Returns:
347 Whether the fetch was successful.
David Pursehousec1b86a22012-11-14 11:36:51 +0900348 """
349 # We'll set to true once we've locked the lock.
350 did_lock = False
Doug Andersonfc06ced2011-03-16 15:49:18 -0700351
David Pursehousec1b86a22012-11-14 11:36:51 +0900352 # Encapsulate everything in a try/except/finally so that:
353 # - We always set err_event in the case of an exception.
David Pursehousec1b86a22012-11-14 11:36:51 +0900354 # - We always make sure we unlock the lock if we locked it.
David Rileye0684ad2017-04-05 00:02:59 -0700355 start = time.time()
356 success = False
Mike Frysinger7b586f22021-02-23 18:38:39 -0500357 buf = io.StringIO()
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500358 with lock:
359 pm.start(project.name)
David Pursehousec1b86a22012-11-14 11:36:51 +0900360 try:
Doug Andersonfc06ced2011-03-16 15:49:18 -0700361 try:
David Pursehousec1b86a22012-11-14 11:36:51 +0900362 success = project.Sync_NetworkHalf(
David Pursehouseabdf7502020-02-12 14:58:39 +0900363 quiet=opt.quiet,
Mike Frysinger521d01b2020-02-17 01:51:49 -0500364 verbose=opt.verbose,
Mike Frysinger7b586f22021-02-23 18:38:39 -0500365 output_redir=buf,
David Pursehouseabdf7502020-02-12 14:58:39 +0900366 current_branch_only=opt.current_branch_only,
367 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500368 clone_bundle=opt.clone_bundle,
369 tags=opt.tags, archive=self.manifest.IsArchive,
David Pursehouseabdf7502020-02-12 14:58:39 +0900370 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600371 retry_fetches=opt.retry_fetches,
David Pursehouseabdf7502020-02-12 14:58:39 +0900372 prune=opt.prune,
373 clone_filter=clone_filter)
David Pursehousec1b86a22012-11-14 11:36:51 +0900374 self._fetch_times.Set(project, time.time() - start)
Doug Andersonfc06ced2011-03-16 15:49:18 -0700375
David Pursehousec1b86a22012-11-14 11:36:51 +0900376 # Lock around all the rest of the code, since printing, updating a set
377 # and Progress.update() are not thread safe.
378 lock.acquire()
379 did_lock = True
Doug Andersonfc06ced2011-03-16 15:49:18 -0700380
Mike Frysinger7b586f22021-02-23 18:38:39 -0500381 output = buf.getvalue()
382 if opt.verbose and output:
383 pm.update(inc=0, msg=output.rstrip())
384
David Pursehousec1b86a22012-11-14 11:36:51 +0900385 if not success:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800386 err_event.set()
Marc Herbertffb4b892017-04-04 22:03:53 -0700387 print('error: Cannot fetch %s from %s'
388 % (project.name, project.remote.url),
389 file=sys.stderr)
Mike Frysingerd9e5cf02019-08-26 03:12:55 -0400390 if opt.fail_fast:
David Pursehousec1b86a22012-11-14 11:36:51 +0900391 raise _FetchError()
Doug Andersonfc06ced2011-03-16 15:49:18 -0700392
David Pursehousec1b86a22012-11-14 11:36:51 +0900393 fetched.add(project.gitdir)
David Pursehousec1b86a22012-11-14 11:36:51 +0900394 except _FetchError:
Hu Xiuyune9becc02015-11-25 15:52:26 +0800395 pass
Dan Sandlerc5cd4332015-07-31 09:37:53 -0400396 except Exception as e:
David Pursehouse42339d72020-02-12 14:37:15 +0900397 print('error: Cannot fetch %s (%s: %s)'
David Pursehouseabdf7502020-02-12 14:58:39 +0900398 % (project.name, type(e).__name__, str(e)), file=sys.stderr)
David Pursehousec1b86a22012-11-14 11:36:51 +0900399 err_event.set()
400 raise
401 finally:
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500402 if not did_lock:
403 lock.acquire()
404 pm.finish(project.name)
405 lock.release()
David Rileye0684ad2017-04-05 00:02:59 -0700406 finish = time.time()
407 self.event_log.AddSync(project, event_log.TASK_SYNC_NETWORK,
408 start, finish, success)
Roy Lee18afd7f2010-05-09 04:32:08 +0800409
David James8d201162013-10-11 17:03:19 -0700410 return success
411
Mike Frysinger5a033082019-09-23 19:21:20 -0400412 def _Fetch(self, projects, opt, err_event):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700413 fetched = set()
David James89ece422014-01-09 18:51:58 -0800414 lock = _threading.Lock()
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500415 pm = Progress('Fetching', len(projects))
Roy Lee18afd7f2010-05-09 04:32:08 +0800416
David James89ece422014-01-09 18:51:58 -0800417 objdir_project_map = dict()
418 for project in projects:
419 objdir_project_map.setdefault(project.objdir, []).append(project)
David James8d201162013-10-11 17:03:19 -0700420
David James89ece422014-01-09 18:51:58 -0800421 threads = set()
422 sem = _threading.Semaphore(self.jobs)
David James89ece422014-01-09 18:51:58 -0800423 for project_list in objdir_project_map.values():
424 # Check for any errors before running any more tasks.
425 # ...we'll let existing threads finish, though.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500426 if err_event.is_set() and opt.fail_fast:
David James89ece422014-01-09 18:51:58 -0800427 break
Doug Andersonfc06ced2011-03-16 15:49:18 -0700428
David James89ece422014-01-09 18:51:58 -0800429 sem.acquire()
430 kwargs = dict(opt=opt,
431 projects=project_list,
Andrew Wheeler7f1ccfb2016-06-17 16:51:07 -0500432 sem=sem,
David James89ece422014-01-09 18:51:58 -0800433 lock=lock,
434 fetched=fetched,
435 pm=pm,
Xin Li745be2e2019-06-03 11:24:30 -0700436 err_event=err_event,
437 clone_filter=self.manifest.CloneFilter)
David James89ece422014-01-09 18:51:58 -0800438 if self.jobs > 1:
David Pursehousee5913ae2020-02-12 13:56:59 +0900439 t = _threading.Thread(target=self._FetchProjectList,
440 kwargs=kwargs)
David 'Digit' Turnere2126652012-09-05 10:35:06 +0200441 # Ensure that Ctrl-C will not freeze the repo process.
442 t.daemon = True
Roy Lee18afd7f2010-05-09 04:32:08 +0800443 threads.add(t)
444 t.start()
David James89ece422014-01-09 18:51:58 -0800445 else:
446 self._FetchProjectList(**kwargs)
Roy Lee18afd7f2010-05-09 04:32:08 +0800447
David James89ece422014-01-09 18:51:58 -0800448 for t in threads:
449 t.join()
Roy Lee18afd7f2010-05-09 04:32:08 +0800450
Shawn O. Pearce68194f42009-04-10 16:48:52 -0700451 pm.end()
Dave Borowitz67700e92012-10-23 15:00:54 -0700452 self._fetch_times.Save()
Dave Borowitz18857212012-10-23 17:02:59 -0700453
Julien Campergue335f5ef2013-10-16 11:02:35 +0200454 if not self.manifest.IsArchive:
Mike Frysinger5a033082019-09-23 19:21:20 -0400455 self._GCProjects(projects, opt, err_event)
Julien Campergue335f5ef2013-10-16 11:02:35 +0200456
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700457 return fetched
458
Mike Frysingerebf04a42021-02-23 20:48:04 -0500459 def _CheckoutOne(self, opt, project):
Xin Li745be2e2019-06-03 11:24:30 -0700460 """Checkout work tree for one project
461
462 Args:
463 opt: Program options returned from optparse. See _Options().
464 project: Project object for the project to checkout.
Xin Li745be2e2019-06-03 11:24:30 -0700465
466 Returns:
467 Whether the fetch was successful.
468 """
Xin Li745be2e2019-06-03 11:24:30 -0700469 start = time.time()
470 syncbuf = SyncBuffer(self.manifest.manifestProject.config,
471 detach_head=opt.detach_head)
472 success = False
473 try:
Mike Frysingerebf04a42021-02-23 20:48:04 -0500474 project.Sync_LocalHalf(syncbuf, force_sync=opt.force_sync)
475 success = syncbuf.Finish()
476 except Exception as e:
477 print('error: Cannot checkout %s: %s: %s' %
478 (project.name, type(e).__name__, str(e)),
479 file=sys.stderr)
480 raise
Xin Li745be2e2019-06-03 11:24:30 -0700481
Mike Frysingerebf04a42021-02-23 20:48:04 -0500482 if not success:
483 print('error: Cannot checkout %s' % (project.name), file=sys.stderr)
484 finish = time.time()
485 return (success, project, start, finish)
Xin Li745be2e2019-06-03 11:24:30 -0700486
Mike Frysingerebf04a42021-02-23 20:48:04 -0500487 def _Checkout(self, all_projects, opt, err_results):
Xin Li745be2e2019-06-03 11:24:30 -0700488 """Checkout projects listed in all_projects
489
490 Args:
491 all_projects: List of all projects that should be checked out.
492 opt: Program options returned from optparse. See _Options().
Mike Frysingerebf04a42021-02-23 20:48:04 -0500493 err_results: A list of strings, paths to git repos where checkout failed.
Xin Li745be2e2019-06-03 11:24:30 -0700494 """
Mike Frysingerebf04a42021-02-23 20:48:04 -0500495 ret = True
Xin Li745be2e2019-06-03 11:24:30 -0700496
Mike Frysingerebf04a42021-02-23 20:48:04 -0500497 # Only checkout projects with worktrees.
498 all_projects = [x for x in all_projects if x.worktree]
Xin Li745be2e2019-06-03 11:24:30 -0700499
Mike Frysingerfbb95a42021-02-23 17:34:35 -0500500 pm = Progress('Checking out', len(all_projects))
Xin Li745be2e2019-06-03 11:24:30 -0700501
Mike Frysingerebf04a42021-02-23 20:48:04 -0500502 def _ProcessResults(results):
503 for (success, project, start, finish) in results:
504 self.event_log.AddSync(project, event_log.TASK_SYNC_LOCAL,
505 start, finish, success)
506 # Check for any errors before running any more tasks.
507 # ...we'll let existing threads finish, though.
508 if not success:
509 err_results.append(project.relpath)
510 if opt.fail_fast:
511 return False
512 pm.update(msg=project.name)
513 return True
Xin Li745be2e2019-06-03 11:24:30 -0700514
Mike Frysingerebf04a42021-02-23 20:48:04 -0500515 # NB: Multiprocessing is heavy, so don't spin it up for one job.
516 if len(all_projects) == 1 or opt.jobs == 1:
517 if not _ProcessResults(self._CheckoutOne(opt, x) for x in all_projects):
518 ret = False
519 else:
520 with multiprocessing.Pool(opt.jobs) as pool:
521 results = pool.imap_unordered(
522 functools.partial(self._CheckoutOne, opt),
523 all_projects,
524 chunksize=WORKER_BATCH_SIZE)
525 if not _ProcessResults(results):
526 ret = False
527 pool.close()
Xin Li745be2e2019-06-03 11:24:30 -0700528
529 pm.end()
Xin Li745be2e2019-06-03 11:24:30 -0700530
Mike Frysinger511a0e52021-03-12 20:03:51 -0500531 return ret and not err_results
Mike Frysingerebf04a42021-02-23 20:48:04 -0500532
Mike Frysinger5a033082019-09-23 19:21:20 -0400533 def _GCProjects(self, projects, opt, err_event):
Gabe Black2ff30292014-10-09 17:54:35 -0700534 gc_gitdirs = {}
David James8d201162013-10-11 17:03:19 -0700535 for project in projects:
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500536 # Make sure pruning never kicks in with shared projects.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500537 if (not project.use_git_worktrees and
David Pursehouseaa611a22020-02-20 10:47:26 +0900538 len(project.manifest.GetProjectsWithName(project.name)) > 1):
Anders Björklund2a2da802021-01-18 10:32:36 +0100539 if not opt.quiet:
540 print('%s: Shared project %s found, disabling pruning.' %
541 (project.relpath, project.name))
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500542 if git_require((2, 7, 0)):
Mike Frysingerf81c72e2020-02-19 15:50:00 -0500543 project.EnableRepositoryExtension('preciousObjects')
Mike Frysinger5f2b0452020-02-11 03:23:24 -0500544 else:
545 # This isn't perfect, but it's the best we can do with old git.
546 print('%s: WARNING: shared projects are unreliable when using old '
547 'versions of git; please upgrade to git-2.7.0+.'
548 % (project.relpath,),
549 file=sys.stderr)
550 project.config.SetString('gc.pruneExpire', 'never')
Gabe Black2ff30292014-10-09 17:54:35 -0700551 gc_gitdirs[project.gitdir] = project.bare_git
David James8d201162013-10-11 17:03:19 -0700552
Mike Frysinger6f1c6262020-02-04 00:09:23 -0500553 if multiprocessing:
Dave Borowitz18857212012-10-23 17:02:59 -0700554 cpu_count = multiprocessing.cpu_count()
555 else:
556 cpu_count = 1
557 jobs = min(self.jobs, cpu_count)
558
559 if jobs < 2:
Gabe Black2ff30292014-10-09 17:54:35 -0700560 for bare_git in gc_gitdirs.values():
David James8d201162013-10-11 17:03:19 -0700561 bare_git.gc('--auto')
Dave Borowitz18857212012-10-23 17:02:59 -0700562 return
563
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400564 config = {'pack.threads': cpu_count // jobs if cpu_count > jobs else 1}
Dave Borowitz18857212012-10-23 17:02:59 -0700565
566 threads = set()
567 sem = _threading.Semaphore(jobs)
Dave Borowitz18857212012-10-23 17:02:59 -0700568
David James8d201162013-10-11 17:03:19 -0700569 def GC(bare_git):
Dave Borowitz18857212012-10-23 17:02:59 -0700570 try:
571 try:
David James8d201162013-10-11 17:03:19 -0700572 bare_git.gc('--auto', config=config)
Dave Borowitz18857212012-10-23 17:02:59 -0700573 except GitError:
574 err_event.set()
David Pursehouse145e35b2020-02-12 15:40:47 +0900575 except Exception:
Dave Borowitz18857212012-10-23 17:02:59 -0700576 err_event.set()
577 raise
578 finally:
579 sem.release()
580
Gabe Black2ff30292014-10-09 17:54:35 -0700581 for bare_git in gc_gitdirs.values():
Mike Frysingerbe24a542021-02-23 03:24:12 -0500582 if err_event.is_set() and opt.fail_fast:
Dave Borowitz18857212012-10-23 17:02:59 -0700583 break
584 sem.acquire()
David James8d201162013-10-11 17:03:19 -0700585 t = _threading.Thread(target=GC, args=(bare_git,))
Dave Borowitz18857212012-10-23 17:02:59 -0700586 t.daemon = True
587 threads.add(t)
588 t.start()
589
590 for t in threads:
591 t.join()
592
Tim Kilbourn07669002013-03-08 15:02:49 -0800593 def _ReloadManifest(self, manifest_name=None):
594 if manifest_name:
595 # Override calls _Unload already
596 self.manifest.Override(manifest_name)
597 else:
598 self.manifest._Unload()
599
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500600 def UpdateProjectList(self, opt):
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700601 new_project_paths = []
Colin Cross5acde752012-03-28 20:15:45 -0700602 for project in self.GetProjects(None, missing_ok=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700603 if project.relpath:
604 new_project_paths.append(project.relpath)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700605 file_name = 'project.list'
Mike Frysingere3315bb2021-02-09 23:45:28 -0500606 file_path = os.path.join(self.repodir, file_name)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700607 old_project_paths = []
608
609 if os.path.exists(file_path):
Mike Frysinger3164d402019-11-11 05:40:22 -0500610 with open(file_path, 'r') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700611 old_project_paths = fd.read().split('\n')
Kuang-che Wu0d9b16d2019-04-06 00:49:47 +0800612 # In reversed order, so subfolders are deleted before parent folder.
613 for path in sorted(old_project_paths, reverse=True):
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700614 if not path:
615 continue
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700616 if path not in new_project_paths:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900617 # If the path has already been deleted, we don't need to do it
Dan Willemsen43507912016-09-01 16:26:02 -0700618 gitdir = os.path.join(self.manifest.topdir, path, '.git')
619 if os.path.exists(gitdir):
David Pursehousec1b86a22012-11-14 11:36:51 +0900620 project = Project(
David Pursehouseabdf7502020-02-12 14:58:39 +0900621 manifest=self.manifest,
622 name=path,
623 remote=RemoteSpec('origin'),
624 gitdir=gitdir,
625 objdir=gitdir,
Mike Frysingerc0d18662020-02-19 19:19:18 -0500626 use_git_worktrees=os.path.isfile(gitdir),
David Pursehouseabdf7502020-02-12 14:58:39 +0900627 worktree=os.path.join(self.manifest.topdir, path),
628 relpath=path,
629 revisionExpr='HEAD',
630 revisionId=None,
631 groups=None)
Mike Frysingerc0d18662020-02-19 19:19:18 -0500632 if not project.DeleteWorktree(
David Pursehouseaa611a22020-02-20 10:47:26 +0900633 quiet=opt.quiet,
634 force=opt.force_remove_dirty):
Mike Frysingera850ca22019-08-07 17:19:24 -0400635 return 1
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700636
Shawn O. Pearce9fb29ce2009-06-04 20:41:02 -0700637 new_project_paths.sort()
Mike Frysinger3164d402019-11-11 05:40:22 -0500638 with open(file_path, 'w') as fd:
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700639 fd.write('\n'.join(new_project_paths))
Shawn O. Pearce3a68bb42009-06-04 16:18:09 -0700640 fd.write('\n')
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700641 return 0
642
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400643 def _SmartSyncSetup(self, opt, smart_sync_manifest_path):
644 if not self.manifest.manifest_server:
645 print('error: cannot smart sync: no manifest server defined in '
646 'manifest', file=sys.stderr)
647 sys.exit(1)
648
649 manifest_server = self.manifest.manifest_server
650 if not opt.quiet:
651 print('Using manifest server %s' % manifest_server)
652
David Pursehouseeeff3532020-02-12 11:24:10 +0900653 if '@' not in manifest_server:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400654 username = None
655 password = None
656 if opt.manifest_server_username and opt.manifest_server_password:
657 username = opt.manifest_server_username
658 password = opt.manifest_server_password
659 else:
660 try:
661 info = netrc.netrc()
662 except IOError:
663 # .netrc file does not exist or could not be opened
664 pass
665 else:
666 try:
667 parse_result = urllib.parse.urlparse(manifest_server)
668 if parse_result.hostname:
669 auth = info.authenticators(parse_result.hostname)
670 if auth:
671 username, _account, password = auth
672 else:
673 print('No credentials found for %s in .netrc'
674 % parse_result.hostname, file=sys.stderr)
675 except netrc.NetrcParseError as e:
676 print('Error parsing .netrc file: %s' % e, file=sys.stderr)
677
678 if (username and password):
679 manifest_server = manifest_server.replace('://', '://%s:%s@' %
680 (username, password),
681 1)
682
683 transport = PersistentTransport(manifest_server)
684 if manifest_server.startswith('persistent-'):
685 manifest_server = manifest_server[len('persistent-'):]
686
687 try:
688 server = xmlrpc.client.Server(manifest_server, transport=transport)
689 if opt.smart_sync:
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800690 branch = self._GetBranch()
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400691
Mike Frysinger56ce3462019-12-04 19:30:48 -0500692 if 'SYNC_TARGET' in os.environ:
Mike Frysingere20da3e2020-03-07 01:53:53 -0500693 target = os.environ['SYNC_TARGET']
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400694 [success, manifest_str] = server.GetApprovedManifest(branch, target)
Mike Frysinger56ce3462019-12-04 19:30:48 -0500695 elif ('TARGET_PRODUCT' in os.environ and
696 'TARGET_BUILD_VARIANT' in os.environ):
Mike Frysingere20da3e2020-03-07 01:53:53 -0500697 target = '%s-%s' % (os.environ['TARGET_PRODUCT'],
698 os.environ['TARGET_BUILD_VARIANT'])
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400699 [success, manifest_str] = server.GetApprovedManifest(branch, target)
700 else:
701 [success, manifest_str] = server.GetApprovedManifest(branch)
702 else:
703 assert(opt.smart_tag)
704 [success, manifest_str] = server.GetManifest(opt.smart_tag)
705
706 if success:
707 manifest_name = os.path.basename(smart_sync_manifest_path)
708 try:
Mike Frysinger3164d402019-11-11 05:40:22 -0500709 with open(smart_sync_manifest_path, 'w') as f:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400710 f.write(manifest_str)
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400711 except IOError as e:
712 print('error: cannot write manifest to %s:\n%s'
713 % (smart_sync_manifest_path, e),
714 file=sys.stderr)
715 sys.exit(1)
716 self._ReloadManifest(manifest_name)
717 else:
718 print('error: manifest server RPC call failed: %s' %
719 manifest_str, file=sys.stderr)
720 sys.exit(1)
721 except (socket.error, IOError, xmlrpc.client.Fault) as e:
722 print('error: cannot connect to manifest server %s:\n%s'
723 % (self.manifest.manifest_server, e), file=sys.stderr)
724 sys.exit(1)
725 except xmlrpc.client.ProtocolError as e:
726 print('error: cannot connect to manifest server %s:\n%d %s'
727 % (self.manifest.manifest_server, e.errcode, e.errmsg),
728 file=sys.stderr)
729 sys.exit(1)
730
731 return manifest_name
732
Mike Frysingerfb527e32019-08-27 02:34:32 -0400733 def _UpdateManifestProject(self, opt, mp, manifest_name):
734 """Fetch & update the local manifest project."""
735 if not opt.local_only:
736 start = time.time()
Mike Frysinger521d01b2020-02-17 01:51:49 -0500737 success = mp.Sync_NetworkHalf(quiet=opt.quiet, verbose=opt.verbose,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400738 current_branch_only=opt.current_branch_only,
Erwan Yvindc5c4d12019-06-18 13:49:12 +0200739 force_sync=opt.force_sync,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500740 tags=opt.tags,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400741 optimized_fetch=opt.optimized_fetch,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600742 retry_fetches=opt.retry_fetches,
Mike Frysingerfb527e32019-08-27 02:34:32 -0400743 submodules=self.manifest.HasSubmodules,
744 clone_filter=self.manifest.CloneFilter)
745 finish = time.time()
746 self.event_log.AddSync(mp, event_log.TASK_SYNC_NETWORK,
747 start, finish, success)
748
749 if mp.HasChanges:
750 syncbuf = SyncBuffer(mp.config)
751 start = time.time()
752 mp.Sync_LocalHalf(syncbuf, submodules=self.manifest.HasSubmodules)
753 clean = syncbuf.Finish()
754 self.event_log.AddSync(mp, event_log.TASK_SYNC_LOCAL,
755 start, time.time(), clean)
756 if not clean:
757 sys.exit(1)
758 self._ReloadManifest(opt.manifest_name)
759 if opt.jobs is None:
760 self.jobs = self.manifest.default.sync_j
761
Mike Frysingerae6cb082019-08-27 01:10:59 -0400762 def ValidateOptions(self, opt, args):
763 if opt.force_broken:
764 print('warning: -f/--force-broken is now the default behavior, and the '
765 'options are deprecated', file=sys.stderr)
766 if opt.network_only and opt.detach_head:
767 self.OptionParser.error('cannot combine -n and -d')
768 if opt.network_only and opt.local_only:
769 self.OptionParser.error('cannot combine -n and -l')
770 if opt.manifest_name and opt.smart_sync:
771 self.OptionParser.error('cannot combine -m and -s')
772 if opt.manifest_name and opt.smart_tag:
773 self.OptionParser.error('cannot combine -m and -t')
774 if opt.manifest_server_username or opt.manifest_server_password:
775 if not (opt.smart_sync or opt.smart_tag):
776 self.OptionParser.error('-u and -p may only be combined with -s or -t')
777 if None in [opt.manifest_server_username, opt.manifest_server_password]:
778 self.OptionParser.error('both -u and -p must be given')
779
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700780 def Execute(self, opt, args):
Roy Lee18afd7f2010-05-09 04:32:08 +0800781 if opt.jobs:
782 self.jobs = opt.jobs
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700783 if self.jobs > 1:
784 soft_limit, _ = _rlimit_nofile()
Mike Frysinger0c0e9342019-06-13 12:42:39 -0400785 self.jobs = min(self.jobs, (soft_limit - 5) // 3)
Shawn O. Pearce97d2b2f2011-09-22 17:23:41 -0700786
Mike Frysinger521d01b2020-02-17 01:51:49 -0500787 opt.quiet = opt.output_mode is False
788 opt.verbose = opt.output_mode is True
789
Chris Wolfee9dc3b32012-01-26 11:36:18 -0500790 if opt.manifest_name:
791 self.manifest.Override(opt.manifest_name)
Shawn O. Pearce3e768c92009-04-10 16:59:36 -0700792
Chirayu Desaia892b102013-06-11 14:18:46 +0530793 manifest_name = opt.manifest_name
David Pursehouse59b41742015-05-07 14:36:09 +0900794 smart_sync_manifest_path = os.path.join(
David Pursehouseabdf7502020-02-12 14:58:39 +0900795 self.manifest.manifestProject.worktree, 'smart_sync_override.xml')
Chirayu Desaia892b102013-06-11 14:18:46 +0530796
Xin Lid79a4bc2020-05-20 16:03:45 -0700797 if opt.clone_bundle is None:
798 opt.clone_bundle = self.manifest.CloneBundle
799
Victor Boivie08c880d2011-04-19 10:32:52 +0200800 if opt.smart_sync or opt.smart_tag:
Mike Frysinger01d6c3c2019-08-27 01:56:43 -0400801 manifest_name = self._SmartSyncSetup(opt, smart_sync_manifest_path)
802 else:
David Pursehouse59b41742015-05-07 14:36:09 +0900803 if os.path.isfile(smart_sync_manifest_path):
804 try:
Renaud Paquay010fed72016-11-11 14:25:29 -0800805 platform_utils.remove(smart_sync_manifest_path)
David Pursehouse59b41742015-05-07 14:36:09 +0900806 except OSError as e:
807 print('error: failed to remove existing smart sync override manifest: %s' %
808 e, file=sys.stderr)
Nico Sallembiena1bfd2c2010-04-06 10:40:01 -0700809
Mike Frysinger5a033082019-09-23 19:21:20 -0400810 err_event = _threading.Event()
811
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700812 rp = self.manifest.repoProject
813 rp.PreSync()
Mike Frysinger23c900f2020-02-29 02:52:44 -0500814 cb = rp.CurrentBranch
815 if cb:
816 base = rp.GetBranch(cb).merge
817 if not base or not base.startswith('refs/heads/'):
818 print('warning: repo is not tracking a remote branch, so it will not '
Mike Frysinger58ac1672020-03-14 14:35:26 -0400819 'receive updates; run `repo init --repo-rev=stable` to fix.',
Mike Frysinger23c900f2020-02-29 02:52:44 -0500820 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700821
822 mp = self.manifest.manifestProject
823 mp.PreSync()
824
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800825 if opt.repo_upgraded:
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700826 _PostRepoUpgrade(self.manifest, quiet=opt.quiet)
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -0800827
Fredrik de Grootcc960972019-11-22 09:04:31 +0100828 if not opt.mp_update:
829 print('Skipping update of local manifest project.')
830 else:
831 self._UpdateManifestProject(opt, mp, manifest_name)
Simran Basib9a1b732015-08-20 12:19:28 -0700832
Raman Tenneti55d6a5a2021-02-24 14:37:01 -0800833 if (opt.use_superproject or
834 self.manifest.manifestProject.config.GetBoolean(
835 'repo.superproject')):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800836 manifest_name = self._UpdateProjectsRevisionId(opt, args)
837
Simran Basib9a1b732015-08-20 12:19:28 -0700838 if self.gitc_manifest:
839 gitc_manifest_projects = self.GetProjects(args,
Simran Basib9a1b732015-08-20 12:19:28 -0700840 missing_ok=True)
841 gitc_projects = []
842 opened_projects = []
843 for project in gitc_manifest_projects:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700844 if project.relpath in self.gitc_manifest.paths and \
845 self.gitc_manifest.paths[project.relpath].old_revision:
846 opened_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700847 else:
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700848 gitc_projects.append(project.relpath)
Simran Basib9a1b732015-08-20 12:19:28 -0700849
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700850 if not args:
851 gitc_projects = None
852
853 if gitc_projects != [] and not opt.local_only:
Simran Basib9a1b732015-08-20 12:19:28 -0700854 print('Updating GITC client: %s' % self.gitc_manifest.gitc_client_name)
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700855 manifest = GitcManifest(self.repodir, self.gitc_manifest.gitc_client_name)
856 if manifest_name:
857 manifest.Override(manifest_name)
858 else:
859 manifest.Override(self.manifest.manifestFile)
860 gitc_utils.generate_gitc_manifest(self.gitc_manifest,
861 manifest,
Simran Basib9a1b732015-08-20 12:19:28 -0700862 gitc_projects)
863 print('GITC client successfully synced.')
864
865 # The opened projects need to be synced as normal, therefore we
866 # generate a new args list to represent the opened projects.
Dan Willemsen5ea32d12015-09-08 13:27:20 -0700867 # TODO: make this more reliable -- if there's a project name/path overlap,
868 # this may choose the wrong project.
David Pursehouse3bcd3052017-07-10 22:42:22 +0900869 args = [os.path.relpath(self.manifest.paths[path].worktree, os.getcwd())
870 for path in opened_projects]
Simran Basib9a1b732015-08-20 12:19:28 -0700871 if not args:
872 return
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800873 all_projects = self.GetProjects(args,
874 missing_ok=True,
875 submodules_ok=opt.fetch_submodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700876
Mike Frysinger5a033082019-09-23 19:21:20 -0400877 err_network_sync = False
878 err_update_projects = False
Mike Frysinger5a033082019-09-23 19:21:20 -0400879
Dave Borowitz67700e92012-10-23 15:00:54 -0700880 self._fetch_times = _FetchTimes(self.manifest)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700881 if not opt.local_only:
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700882 to_fetch = []
883 now = time.time()
Dave Borowitz67700e92012-10-23 15:00:54 -0700884 if _ONE_DAY_S <= (now - rp.LastFetch):
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700885 to_fetch.append(rp)
David Pursehouse8a68ff92012-09-24 12:15:13 +0900886 to_fetch.extend(all_projects)
Dave Borowitz67700e92012-10-23 15:00:54 -0700887 to_fetch.sort(key=self._fetch_times.Get, reverse=True)
Shawn O. Pearcef6906872009-04-18 10:49:00 -0700888
Mike Frysinger5a033082019-09-23 19:21:20 -0400889 fetched = self._Fetch(to_fetch, opt, err_event)
890
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500891 _PostRepoFetch(rp, opt.repo_verify)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700892 if opt.network_only:
893 # bail out now; the rest touches the working tree
Mike Frysingerbe24a542021-02-23 03:24:12 -0500894 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400895 print('\nerror: Exited sync due to fetch errors.\n', file=sys.stderr)
896 sys.exit(1)
Shawn O. Pearceb1562fa2009-04-10 17:04:08 -0700897 return
898
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800899 # Iteratively fetch missing and/or nested unregistered submodules
900 previously_missing_set = set()
901 while True:
Victor Boivie53a6c5d2013-03-19 12:20:52 +0100902 self._ReloadManifest(manifest_name)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800903 all_projects = self.GetProjects(args,
904 missing_ok=True,
905 submodules_ok=opt.fetch_submodules)
906 missing = []
907 for project in all_projects:
908 if project.gitdir not in fetched:
909 missing.append(project)
910 if not missing:
911 break
912 # Stop us from non-stopped fetching actually-missing repos: If set of
913 # missing repos has not been changed from last fetch, we break.
914 missing_set = set(p.name for p in missing)
915 if previously_missing_set == missing_set:
916 break
917 previously_missing_set = missing_set
Mike Frysinger5a033082019-09-23 19:21:20 -0400918 fetched.update(self._Fetch(missing, opt, err_event))
919
920 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500921 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400922 err_network_sync = True
923 if opt.fail_fast:
924 print('\nerror: Exited sync due to fetch errors.\n'
925 'Local checkouts *not* updated. Resolve network issues & '
926 'retry.\n'
927 '`repo sync -l` will update some local checkouts.',
928 file=sys.stderr)
929 sys.exit(1)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800930
Julien Campergue335f5ef2013-10-16 11:02:35 +0200931 if self.manifest.IsMirror or self.manifest.IsArchive:
Shawn O. Pearcecd1d7ff2009-06-04 16:15:53 -0700932 # bail out now, we have no working tree
933 return
934
Oleksii Okolielovd3c0f592018-12-17 19:23:44 -0500935 if self.UpdateProjectList(opt):
Mike Frysinger5a033082019-09-23 19:21:20 -0400936 err_event.set()
937 err_update_projects = True
938 if opt.fail_fast:
939 print('\nerror: Local checkouts *not* updated.', file=sys.stderr)
940 sys.exit(1)
Jaikumar Ganesh4f2517f2009-06-01 21:10:33 -0700941
Mike Frysinger5a033082019-09-23 19:21:20 -0400942 err_results = []
Mike Frysingerebf04a42021-02-23 20:48:04 -0500943 # NB: We don't exit here because this is the last step.
944 err_checkout = not self._Checkout(all_projects, opt, err_results)
945 if err_checkout:
946 err_event.set()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700947
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700948 # If there's a notice that's supposed to print at the end of the sync, print
949 # it now...
950 if self.manifest.notice:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700951 print(self.manifest.notice)
Doug Anderson2b8db3c2010-11-01 15:08:06 -0700952
Mike Frysinger5a033082019-09-23 19:21:20 -0400953 # If we saw an error, exit with code 1 so that other scripts can check.
Mike Frysingerbe24a542021-02-23 03:24:12 -0500954 if err_event.is_set():
Mike Frysinger5a033082019-09-23 19:21:20 -0400955 print('\nerror: Unable to fully sync the tree.', file=sys.stderr)
956 if err_network_sync:
957 print('error: Downloading network changes failed.', file=sys.stderr)
958 if err_update_projects:
959 print('error: Updating local project lists failed.', file=sys.stderr)
960 if err_checkout:
961 print('error: Checking out local projects failed.', file=sys.stderr)
962 if err_results:
963 print('Failing repos:\n%s' % '\n'.join(err_results), file=sys.stderr)
964 print('Try re-running with "-j1 --fail-fast" to exit at the first error.',
965 file=sys.stderr)
966 sys.exit(1)
967
Mike Frysingere19d9e12020-02-12 11:23:32 -0500968 if not opt.quiet:
969 print('repo sync has finished successfully.')
970
David Pursehouse819827a2020-02-12 15:20:19 +0900971
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700972def _PostRepoUpgrade(manifest, quiet=False):
Conley Owens094cdbe2014-01-30 15:09:59 -0800973 wrapper = Wrapper()
Conley Owensc9129d92012-10-01 16:12:28 -0700974 if wrapper.NeedSetupGnuPG():
Shawn O. Pearce80d2ceb2012-10-26 12:23:05 -0700975 wrapper.SetupGnuPG(quiet)
Conley Owensf2fe2d92014-01-29 13:53:43 -0800976 for project in manifest.projects:
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700977 if project.Exists:
978 project.PostRepoUpgrade()
979
David Pursehouse819827a2020-02-12 15:20:19 +0900980
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500981def _PostRepoFetch(rp, repo_verify=True, verbose=False):
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700982 if rp.HasChanges:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700983 print('info: A new version of repo is available', file=sys.stderr)
984 print(file=sys.stderr)
Mike Frysingerc58ec4d2020-02-17 14:36:08 -0500985 if not repo_verify or _VerifyTag(rp):
Shawn O. Pearce350cde42009-04-16 11:21:18 -0700986 syncbuf = SyncBuffer(rp.config)
987 rp.Sync_LocalHalf(syncbuf)
988 if not syncbuf.Finish():
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700989 sys.exit(1)
Sarah Owenscecd1d82012-11-01 22:59:27 -0700990 print('info: Restarting repo with latest version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700991 raise RepoChangedException(['--repo-upgraded'])
992 else:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700993 print('warning: Skipped upgrade to unverified version', file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700994 else:
995 if verbose:
Sarah Owenscecd1d82012-11-01 22:59:27 -0700996 print('repo version %s is current' % rp.work_git.describe(HEAD),
997 file=sys.stderr)
Shawn O. Pearcee756c412009-04-13 11:51:15 -0700998
David Pursehouse819827a2020-02-12 15:20:19 +0900999
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001000def _VerifyTag(project):
1001 gpg_dir = os.path.expanduser('~/.repoconfig/gnupg')
1002 if not os.path.exists(gpg_dir):
Sarah Owenscecd1d82012-11-01 22:59:27 -07001003 print('warning: GnuPG was not available during last "repo init"\n'
1004 'warning: Cannot automatically authenticate repo."""',
1005 file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001006 return True
1007
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001008 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001009 cur = project.bare_git.describe(project.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001010 except GitError:
1011 cur = None
1012
1013 if not cur \
1014 or re.compile(r'^.*-[0-9]{1,}-g[0-9a-f]{1,}$').match(cur):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001015 rev = project.revisionExpr
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001016 if rev.startswith(R_HEADS):
1017 rev = rev[len(R_HEADS):]
1018
Sarah Owenscecd1d82012-11-01 22:59:27 -07001019 print(file=sys.stderr)
1020 print("warning: project '%s' branch '%s' is not signed"
1021 % (project.name, rev), file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001022 return False
1023
Shawn O. Pearcef18cb762010-12-07 11:41:05 -08001024 env = os.environ.copy()
Mike Frysinger56ce3462019-12-04 19:30:48 -05001025 env['GIT_DIR'] = project.gitdir
1026 env['GNUPGHOME'] = gpg_dir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001027
1028 cmd = [GIT, 'tag', '-v', cur]
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001029 result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
1030 env=env, check=False)
1031 if result.returncode:
Sarah Owenscecd1d82012-11-01 22:59:27 -07001032 print(file=sys.stderr)
Mike Frysingerfb21d6a2021-02-16 02:37:55 -05001033 print(result.stdout, file=sys.stderr)
Sarah Owenscecd1d82012-11-01 22:59:27 -07001034 print(file=sys.stderr)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001035 return False
1036 return True
Dave Borowitz67700e92012-10-23 15:00:54 -07001037
David Rileye0684ad2017-04-05 00:02:59 -07001038
Dave Borowitz67700e92012-10-23 15:00:54 -07001039class _FetchTimes(object):
Dave Borowitzd9478582012-10-23 16:35:39 -07001040 _ALPHA = 0.5
1041
Dave Borowitz67700e92012-10-23 15:00:54 -07001042 def __init__(self, manifest):
Anthony King85b24ac2014-05-06 15:57:48 +01001043 self._path = os.path.join(manifest.repodir, '.repo_fetchtimes.json')
Dave Borowitz67700e92012-10-23 15:00:54 -07001044 self._times = None
Dave Borowitzd9478582012-10-23 16:35:39 -07001045 self._seen = set()
Dave Borowitz67700e92012-10-23 15:00:54 -07001046
1047 def Get(self, project):
1048 self._Load()
1049 return self._times.get(project.name, _ONE_DAY_S)
1050
1051 def Set(self, project, t):
Dave Borowitzd9478582012-10-23 16:35:39 -07001052 self._Load()
1053 name = project.name
1054 old = self._times.get(name, t)
1055 self._seen.add(name)
1056 a = self._ALPHA
David Pursehouse54a4e602020-02-12 14:31:05 +09001057 self._times[name] = (a * t) + ((1 - a) * old)
Dave Borowitz67700e92012-10-23 15:00:54 -07001058
1059 def _Load(self):
1060 if self._times is None:
1061 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001062 with open(self._path) as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001063 self._times = json.load(f)
Anthony King85b24ac2014-05-06 15:57:48 +01001064 except (IOError, ValueError):
1065 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001066 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001067 except OSError:
1068 pass
1069 self._times = {}
Dave Borowitz67700e92012-10-23 15:00:54 -07001070
1071 def Save(self):
1072 if self._times is None:
1073 return
Dave Borowitzd9478582012-10-23 16:35:39 -07001074
1075 to_delete = []
1076 for name in self._times:
1077 if name not in self._seen:
1078 to_delete.append(name)
1079 for name in to_delete:
1080 del self._times[name]
1081
Dave Borowitz67700e92012-10-23 15:00:54 -07001082 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001083 with open(self._path, 'w') as f:
Anthony King85b24ac2014-05-06 15:57:48 +01001084 json.dump(self._times, f, indent=2)
Anthony King85b24ac2014-05-06 15:57:48 +01001085 except (IOError, TypeError):
1086 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001087 platform_utils.remove(self._path)
Anthony King85b24ac2014-05-06 15:57:48 +01001088 except OSError:
1089 pass
Dan Willemsen0745bb22015-08-17 13:41:45 -07001090
1091# This is a replacement for xmlrpc.client.Transport using urllib2
1092# and supporting persistent-http[s]. It cannot change hosts from
1093# request to request like the normal transport, the real url
1094# is passed during initialization.
David Pursehouse819827a2020-02-12 15:20:19 +09001095
1096
Dan Willemsen0745bb22015-08-17 13:41:45 -07001097class PersistentTransport(xmlrpc.client.Transport):
1098 def __init__(self, orig_host):
1099 self.orig_host = orig_host
1100
1101 def request(self, host, handler, request_body, verbose=False):
1102 with GetUrlCookieFile(self.orig_host, not verbose) as (cookiefile, proxy):
1103 # Python doesn't understand cookies with the #HttpOnly_ prefix
1104 # Since we're only using them for HTTP, copy the file temporarily,
1105 # stripping those prefixes away.
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001106 if cookiefile:
Collin Fijalkoviche1191b32020-02-18 10:57:32 -08001107 tmpcookiefile = tempfile.NamedTemporaryFile(mode='w')
David Pursehouse4c5f74e2015-10-02 11:10:10 +09001108 tmpcookiefile.write("# HTTP Cookie File")
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001109 try:
1110 with open(cookiefile) as f:
1111 for line in f:
1112 if line.startswith("#HttpOnly_"):
1113 line = line[len("#HttpOnly_"):]
1114 tmpcookiefile.write(line)
1115 tmpcookiefile.flush()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001116
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001117 cookiejar = cookielib.MozillaCookieJar(tmpcookiefile.name)
David Pursehouseb1ad2192015-09-30 10:35:43 +09001118 try:
1119 cookiejar.load()
1120 except cookielib.LoadError:
1121 cookiejar = cookielib.CookieJar()
Dan Willemsen3010e5b2015-08-20 10:09:20 -07001122 finally:
1123 tmpcookiefile.close()
1124 else:
1125 cookiejar = cookielib.CookieJar()
Dan Willemsen0745bb22015-08-17 13:41:45 -07001126
1127 proxyhandler = urllib.request.ProxyHandler
1128 if proxy:
1129 proxyhandler = urllib.request.ProxyHandler({
1130 "http": proxy,
David Pursehouse54a4e602020-02-12 14:31:05 +09001131 "https": proxy})
Dan Willemsen0745bb22015-08-17 13:41:45 -07001132
1133 opener = urllib.request.build_opener(
1134 urllib.request.HTTPCookieProcessor(cookiejar),
1135 proxyhandler)
1136
1137 url = urllib.parse.urljoin(self.orig_host, handler)
1138 parse_results = urllib.parse.urlparse(url)
1139
1140 scheme = parse_results.scheme
1141 if scheme == 'persistent-http':
1142 scheme = 'http'
1143 if scheme == 'persistent-https':
1144 # If we're proxying through persistent-https, use http. The
1145 # proxy itself will do the https.
1146 if proxy:
1147 scheme = 'http'
1148 else:
1149 scheme = 'https'
1150
1151 # Parse out any authentication information using the base class
1152 host, extra_headers, _ = self.get_host_info(parse_results.netloc)
1153
1154 url = urllib.parse.urlunparse((
1155 scheme,
1156 host,
1157 parse_results.path,
1158 parse_results.params,
1159 parse_results.query,
1160 parse_results.fragment))
1161
1162 request = urllib.request.Request(url, request_body)
1163 if extra_headers is not None:
1164 for (name, header) in extra_headers:
1165 request.add_header(name, header)
1166 request.add_header('Content-Type', 'text/xml')
1167 try:
1168 response = opener.open(request)
1169 except urllib.error.HTTPError as e:
1170 if e.code == 501:
1171 # We may have been redirected through a login process
1172 # but our POST turned into a GET. Retry.
1173 response = opener.open(request)
1174 else:
1175 raise
1176
1177 p, u = xmlrpc.client.getparser()
1178 while 1:
1179 data = response.read(1024)
1180 if not data:
1181 break
1182 p.feed(data)
1183 p.close()
1184 return u.close()
1185
1186 def close(self):
1187 pass