blob: 69a4d1fe9c6f79e05c917bb87b910566fe13357c [file] [log] [blame]
Raman Tenneti6a872c92021-01-14 19:17:50 -08001# Copyright (C) 2021 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
Raman Tenneti21dce3d2021-02-09 00:26:31 -080015"""Provide functionality to get all projects and their commit ids from Superproject.
Raman Tenneti6a872c92021-01-14 19:17:50 -080016
17For more information on superproject, check out:
18https://en.wikibooks.org/wiki/Git/Submodules_and_Superprojects
19
20Examples:
LaMont Jonesff6b1da2022-06-01 21:03:34 +000021 superproject = Superproject(manifest, name, remote, revision)
Raman Tenneti784e16f2021-06-11 17:29:45 -070022 UpdateProjectsResult = superproject.UpdateProjectsRevisionId(projects)
Raman Tenneti6a872c92021-01-14 19:17:50 -080023"""
24
Raman Tenneticeba2dd2021-02-22 16:54:56 -080025import hashlib
Xin Li0cb6e922021-06-16 10:19:00 -070026import functools
Raman Tenneti6a872c92021-01-14 19:17:50 -080027import os
28import sys
Xin Li0cb6e922021-06-16 10:19:00 -070029import time
Raman Tenneti784e16f2021-06-11 17:29:45 -070030from typing import NamedTuple
Raman Tenneti6a872c92021-01-14 19:17:50 -080031
Raman Tennetie253b432021-06-02 10:05:54 -070032from git_command import git_require, GitCommand
Xin Li0cb6e922021-06-16 10:19:00 -070033from git_config import RepoConfig
Daniel Kutik035f22a2022-12-13 12:34:23 +010034from git_refs import GitRefs
Raman Tenneti6a872c92021-01-14 19:17:50 -080035
Raman Tenneti8d43dea2021-02-07 16:30:27 -080036_SUPERPROJECT_GIT_NAME = 'superproject.git'
37_SUPERPROJECT_MANIFEST_NAME = 'superproject_override.xml'
38
Raman Tenneti6a872c92021-01-14 19:17:50 -080039
Raman Tenneti784e16f2021-06-11 17:29:45 -070040class SyncResult(NamedTuple):
41 """Return the status of sync and whether caller should exit."""
42
43 # Whether the superproject sync was successful.
44 success: bool
45 # Whether the caller should exit.
46 fatal: bool
47
48
49class CommitIdsResult(NamedTuple):
50 """Return the commit ids and whether caller should exit."""
51
52 # A dictionary with the projects/commit ids on success, otherwise None.
53 commit_ids: dict
54 # Whether the caller should exit.
55 fatal: bool
56
57
58class UpdateProjectsResult(NamedTuple):
59 """Return the overriding manifest file and whether caller should exit."""
60
Raman Tennetib55769a2021-08-13 11:47:24 -070061 # Path name of the overriding manifest file if successful, otherwise None.
Raman Tenneti784e16f2021-06-11 17:29:45 -070062 manifest_path: str
63 # Whether the caller should exit.
64 fatal: bool
65
66
Raman Tenneti6a872c92021-01-14 19:17:50 -080067class Superproject(object):
Raman Tenneti21dce3d2021-02-09 00:26:31 -080068 """Get commit ids from superproject.
Raman Tenneti6a872c92021-01-14 19:17:50 -080069
Raman Tenneticeba2dd2021-02-22 16:54:56 -080070 Initializes a local copy of a superproject for the manifest. This allows
71 lookup of commit ids for all projects. It contains _project_commit_ids which
72 is a dictionary with project/commit id entries.
Raman Tenneti6a872c92021-01-14 19:17:50 -080073 """
LaMont Jonesd56e2eb2022-04-07 18:14:46 +000074 def __init__(self, manifest, name, remote, revision,
75 superproject_dir='exp-superproject'):
Raman Tenneti6a872c92021-01-14 19:17:50 -080076 """Initializes superproject.
77
78 Args:
Raman Tenneti21dce3d2021-02-09 00:26:31 -080079 manifest: A Manifest object that is to be written to a file.
LaMont Jonesd56e2eb2022-04-07 18:14:46 +000080 name: The unique name of the superproject
81 remote: The RemoteSpec for the remote.
82 revision: The name of the git branch to track.
83 superproject_dir: Relative path under |manifest.subdir| to checkout
84 superproject.
Raman Tenneti6a872c92021-01-14 19:17:50 -080085 """
Raman Tenneti21dce3d2021-02-09 00:26:31 -080086 self._project_commit_ids = None
87 self._manifest = manifest
LaMont Jonesd56e2eb2022-04-07 18:14:46 +000088 self.name = name
89 self.remote = remote
90 self.revision = self._branch = revision
91 self._repodir = manifest.repodir
Raman Tenneti6a872c92021-01-14 19:17:50 -080092 self._superproject_dir = superproject_dir
LaMont Jonescc879a92021-11-18 22:40:18 +000093 self._superproject_path = manifest.SubmanifestInfoDir(manifest.path_prefix,
94 superproject_dir)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -080095 self._manifest_path = os.path.join(self._superproject_path,
Raman Tenneti8d43dea2021-02-07 16:30:27 -080096 _SUPERPROJECT_MANIFEST_NAME)
LaMont Jonesd56e2eb2022-04-07 18:14:46 +000097 git_name = hashlib.md5(remote.name.encode('utf8')).hexdigest() + '-'
98 self._remote_url = remote.url
Raman Tenneticeba2dd2021-02-22 16:54:56 -080099 self._work_git_name = git_name + _SUPERPROJECT_GIT_NAME
100 self._work_git = os.path.join(self._superproject_path, self._work_git_name)
Raman Tenneti6a872c92021-01-14 19:17:50 -0800101
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000102 # The following are command arguemnts, rather than superproject attributes,
103 # and were included here originally. They should eventually become
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000104 # arguments that are passed down from the public methods, instead of being
105 # treated as attributes.
106 self._git_event_log = None
107 self._quiet = False
108 self._print_messages = False
109
110 def SetQuiet(self, value):
111 """Set the _quiet attribute."""
112 self._quiet = value
113
114 def SetPrintMessages(self, value):
115 """Set the _print_messages attribute."""
116 self._print_messages = value
117
Raman Tenneti6a872c92021-01-14 19:17:50 -0800118 @property
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800119 def project_commit_ids(self):
120 """Returns a dictionary of projects and their commit ids."""
121 return self._project_commit_ids
Raman Tenneti6a872c92021-01-14 19:17:50 -0800122
Raman Tennetiae86a462021-07-27 08:54:59 -0700123 @property
124 def manifest_path(self):
125 """Returns the manifest path if the path exists or None."""
126 return self._manifest_path if os.path.exists(self._manifest_path) else None
127
Joanna Wang016a2542023-02-01 15:15:00 -0500128 def _LogMessage(self, fmt, *inputs):
Raman Tenneti8db30d62021-07-06 21:30:06 -0700129 """Logs message to stderr and _git_event_log."""
Joanna Wang016a2542023-02-01 15:15:00 -0500130 message = f'{self._LogMessagePrefix()} {fmt.format(*inputs)}'
Raman Tennetib55769a2021-08-13 11:47:24 -0700131 if self._print_messages:
132 print(message, file=sys.stderr)
Joanna Wang016a2542023-02-01 15:15:00 -0500133 self._git_event_log.ErrorEvent(message, fmt)
Raman Tenneti8db30d62021-07-06 21:30:06 -0700134
Raman Tennetid8e8ae82021-09-15 16:32:33 -0700135 def _LogMessagePrefix(self):
136 """Returns the prefix string to be logged in each log message"""
137 return f'repo superproject branch: {self._branch} url: {self._remote_url}'
138
Joanna Wang016a2542023-02-01 15:15:00 -0500139 def _LogError(self, fmt, *inputs):
Raman Tenneti5637afc2021-08-11 09:26:30 -0700140 """Logs error message to stderr and _git_event_log."""
Joanna Wang016a2542023-02-01 15:15:00 -0500141 self._LogMessage(f'error: {fmt}', *inputs)
Raman Tenneti5637afc2021-08-11 09:26:30 -0700142
Joanna Wang016a2542023-02-01 15:15:00 -0500143 def _LogWarning(self, fmt, *inputs):
Raman Tenneti5637afc2021-08-11 09:26:30 -0700144 """Logs warning message to stderr and _git_event_log."""
Joanna Wang016a2542023-02-01 15:15:00 -0500145 self._LogMessage(f'warning: {fmt}', *inputs)
Raman Tenneti5637afc2021-08-11 09:26:30 -0700146
Raman Tenneticeba2dd2021-02-22 16:54:56 -0800147 def _Init(self):
148 """Sets up a local Git repository to get a copy of a superproject.
Raman Tenneti6a872c92021-01-14 19:17:50 -0800149
150 Returns:
Raman Tenneticeba2dd2021-02-22 16:54:56 -0800151 True if initialization is successful, or False.
Raman Tenneti6a872c92021-01-14 19:17:50 -0800152 """
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800153 if not os.path.exists(self._superproject_path):
154 os.mkdir(self._superproject_path)
Raman Tennetief99ec02021-03-04 10:29:40 -0800155 if not self._quiet and not os.path.exists(self._work_git):
156 print('%s: Performing initial setup for superproject; this might take '
157 'several minutes.' % self._work_git)
Raman Tenneticeba2dd2021-02-22 16:54:56 -0800158 cmd = ['init', '--bare', self._work_git_name]
Raman Tenneti6a872c92021-01-14 19:17:50 -0800159 p = GitCommand(None,
160 cmd,
161 cwd=self._superproject_path,
162 capture_stdout=True,
163 capture_stderr=True)
164 retval = p.Wait()
165 if retval:
Joanna Wang016a2542023-02-01 15:15:00 -0500166 self._LogWarning('git init call failed, command: git {}, '
167 'return code: {}, stderr: {}', cmd, retval, p.stderr)
Raman Tenneti6a872c92021-01-14 19:17:50 -0800168 return False
169 return True
170
Raman Tennetid8e8ae82021-09-15 16:32:33 -0700171 def _Fetch(self):
172 """Fetches a local copy of a superproject for the manifest based on |_remote_url|.
Raman Tenneti9e787532021-02-01 11:47:06 -0800173
174 Returns:
Raman Tenneticeba2dd2021-02-22 16:54:56 -0800175 True if fetch is successful, or False.
Raman Tenneti9e787532021-02-01 11:47:06 -0800176 """
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800177 if not os.path.exists(self._work_git):
Joanna Wang016a2542023-02-01 15:15:00 -0500178 self._LogWarning('git fetch missing directory: {}', self._work_git)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800179 return False
Raman Tennetie253b432021-06-02 10:05:54 -0700180 if not git_require((2, 28, 0)):
Raman Tennetib55769a2021-08-13 11:47:24 -0700181 self._LogWarning('superproject requires a git version 2.28 or later')
Raman Tennetie253b432021-06-02 10:05:54 -0700182 return False
Raman Tennetid8e8ae82021-09-15 16:32:33 -0700183 cmd = ['fetch', self._remote_url, '--depth', '1', '--force', '--no-tags',
184 '--filter', 'blob:none']
Joanna Wang0e4f1e72022-12-08 17:46:28 -0500185
186 # Check if there is a local ref that we can pass to --negotiation-tip.
187 # If this is the first fetch, it does not exist yet.
188 # We use --negotiation-tip to speed up the fetch. Superproject branches do
189 # not share commits. So this lets git know it only needs to send commits
190 # reachable from the specified local refs.
191 rev_commit = GitRefs(self._work_git).get(f'refs/heads/{self.revision}')
192 if rev_commit:
193 cmd.extend(['--negotiation-tip', rev_commit])
194
Raman Tenneticeba2dd2021-02-22 16:54:56 -0800195 if self._branch:
196 cmd += [self._branch + ':' + self._branch]
Raman Tenneti9e787532021-02-01 11:47:06 -0800197 p = GitCommand(None,
198 cmd,
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800199 cwd=self._work_git,
Raman Tenneti9e787532021-02-01 11:47:06 -0800200 capture_stdout=True,
201 capture_stderr=True)
202 retval = p.Wait()
203 if retval:
Joanna Wang016a2542023-02-01 15:15:00 -0500204 self._LogWarning('git fetch call failed, command: git {}, '
205 'return code: {}, stderr: {}', cmd, retval, p.stderr)
Raman Tenneti9e787532021-02-01 11:47:06 -0800206 return False
207 return True
208
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800209 def _LsTree(self):
Raman Tenneticeba2dd2021-02-22 16:54:56 -0800210 """Gets the commit ids for all projects.
Raman Tenneti6a872c92021-01-14 19:17:50 -0800211
212 Works only in git repositories.
213
214 Returns:
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800215 data: data returned from 'git ls-tree ...' instead of None.
Raman Tenneti6a872c92021-01-14 19:17:50 -0800216 """
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800217 if not os.path.exists(self._work_git):
Joanna Wang016a2542023-02-01 15:15:00 -0500218 self._LogWarning('git ls-tree missing directory: {}', self._work_git)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800219 return None
Raman Tenneti6a872c92021-01-14 19:17:50 -0800220 data = None
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800221 branch = 'HEAD' if not self._branch else self._branch
Raman Tennetice64e3d2021-02-08 13:27:41 -0800222 cmd = ['ls-tree', '-z', '-r', branch]
223
Raman Tenneti6a872c92021-01-14 19:17:50 -0800224 p = GitCommand(None,
225 cmd,
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800226 cwd=self._work_git,
Raman Tenneti6a872c92021-01-14 19:17:50 -0800227 capture_stdout=True,
228 capture_stderr=True)
229 retval = p.Wait()
230 if retval == 0:
231 data = p.stdout
232 else:
Joanna Wang016a2542023-02-01 15:15:00 -0500233 self._LogWarning('git ls-tree call failed, command: git {}, '
234 'return code: {}, stderr: {}', cmd, retval, p.stderr)
Raman Tenneti6a872c92021-01-14 19:17:50 -0800235 return data
236
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000237 def Sync(self, git_event_log):
Raman Tenneticeba2dd2021-02-22 16:54:56 -0800238 """Gets a local copy of a superproject for the manifest.
Raman Tenneti6a872c92021-01-14 19:17:50 -0800239
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000240 Args:
241 git_event_log: an EventLog, for git tracing.
242
Raman Tenneti6a872c92021-01-14 19:17:50 -0800243 Returns:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700244 SyncResult
Raman Tenneti6a872c92021-01-14 19:17:50 -0800245 """
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000246 self._git_event_log = git_event_log
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800247 if not self._manifest.superproject:
Joanna Wang016a2542023-02-01 15:15:00 -0500248 self._LogWarning('superproject tag is not defined in manifest: {}',
249 self._manifest.manifestFile)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700250 return SyncResult(False, False)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800251
LaMont Jones2cc3ab72022-04-13 15:58:58 +0000252 _PrintBetaNotice()
253
Raman Tenneti784e16f2021-06-11 17:29:45 -0700254 should_exit = True
Raman Tennetid8e8ae82021-09-15 16:32:33 -0700255 if not self._remote_url:
Joanna Wang016a2542023-02-01 15:15:00 -0500256 self._LogWarning('superproject URL is not defined in manifest: {}',
257 self._manifest.manifestFile)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700258 return SyncResult(False, should_exit)
Raman Tenneti8d43dea2021-02-07 16:30:27 -0800259
Raman Tenneticeba2dd2021-02-22 16:54:56 -0800260 if not self._Init():
Raman Tenneti784e16f2021-06-11 17:29:45 -0700261 return SyncResult(False, should_exit)
Raman Tennetid8e8ae82021-09-15 16:32:33 -0700262 if not self._Fetch():
Raman Tenneti784e16f2021-06-11 17:29:45 -0700263 return SyncResult(False, should_exit)
Raman Tennetief99ec02021-03-04 10:29:40 -0800264 if not self._quiet:
265 print('%s: Initial setup for superproject completed.' % self._work_git)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700266 return SyncResult(True, False)
Raman Tenneti6a872c92021-01-14 19:17:50 -0800267
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800268 def _GetAllProjectsCommitIds(self):
269 """Get commit ids for all projects from superproject and save them in _project_commit_ids.
270
271 Returns:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700272 CommitIdsResult
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800273 """
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000274 sync_result = self.Sync(self._git_event_log)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700275 if not sync_result.success:
276 return CommitIdsResult(None, sync_result.fatal)
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800277
278 data = self._LsTree()
Raman Tenneti6a872c92021-01-14 19:17:50 -0800279 if not data:
Joanna Wang016a2542023-02-01 15:15:00 -0500280 self._LogWarning('git ls-tree failed to return data for manifest: {}',
281 self._manifest.manifestFile)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700282 return CommitIdsResult(None, True)
Raman Tenneti6a872c92021-01-14 19:17:50 -0800283
284 # Parse lines like the following to select lines starting with '160000' and
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800285 # build a dictionary with project path (last element) and its commit id (3rd element).
Raman Tenneti6a872c92021-01-14 19:17:50 -0800286 #
287 # 160000 commit 2c2724cb36cd5a9cec6c852c681efc3b7c6b86ea\tart\x00
288 # 120000 blob acc2cbdf438f9d2141f0ae424cec1d8fc4b5d97f\tbootstrap.bash\x00
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800289 commit_ids = {}
Raman Tenneti6a872c92021-01-14 19:17:50 -0800290 for line in data.split('\x00'):
291 ls_data = line.split(None, 3)
292 if not ls_data:
293 break
294 if ls_data[0] == '160000':
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800295 commit_ids[ls_data[3]] = ls_data[2]
Raman Tenneti6a872c92021-01-14 19:17:50 -0800296
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800297 self._project_commit_ids = commit_ids
Raman Tenneti784e16f2021-06-11 17:29:45 -0700298 return CommitIdsResult(commit_ids, False)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800299
Raman Tennetib55769a2021-08-13 11:47:24 -0700300 def _WriteManifestFile(self):
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800301 """Writes manifest to a file.
302
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800303 Returns:
304 manifest_path: Path name of the file into which manifest is written instead of None.
305 """
306 if not os.path.exists(self._superproject_path):
Joanna Wang016a2542023-02-01 15:15:00 -0500307 self._LogWarning('missing superproject directory: {}', self._superproject_path)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800308 return None
LaMont Jonesa8cf5752022-07-15 20:31:33 +0000309 manifest_str = self._manifest.ToXml(groups=self._manifest.GetGroupsStr(),
310 omit_local=True).toxml()
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800311 manifest_path = self._manifest_path
312 try:
313 with open(manifest_path, 'w', encoding='utf-8') as fp:
314 fp.write(manifest_str)
315 except IOError as e:
Joanna Wang016a2542023-02-01 15:15:00 -0500316 self._LogError('cannot write manifest to : {} {}',
317 manifest_path, e)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800318 return None
319 return manifest_path
320
Raman Tenneti784e16f2021-06-11 17:29:45 -0700321 def _SkipUpdatingProjectRevisionId(self, project):
322 """Checks if a project's revision id needs to be updated or not.
323
324 Revision id for projects from local manifest will not be updated.
325
326 Args:
327 project: project whose revision id is being updated.
328
329 Returns:
330 True if a project's revision id should not be updated, or False,
331 """
332 path = project.relpath
333 if not path:
334 return True
Raman Tenneti1da6f302021-06-28 19:21:38 -0700335 # Skip the project with revisionId.
336 if project.revisionId:
337 return True
Raman Tenneti784e16f2021-06-11 17:29:45 -0700338 # Skip the project if it comes from the local manifest.
LaMont Jones87cce682022-02-14 17:48:31 +0000339 return project.manifest.IsFromLocalManifest(project)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700340
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000341 def UpdateProjectsRevisionId(self, projects, git_event_log):
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800342 """Update revisionId of every project in projects with the commit id.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800343
344 Args:
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000345 projects: a list of projects whose revisionId needs to be updated.
346 git_event_log: an EventLog, for git tracing.
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800347
348 Returns:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700349 UpdateProjectsResult
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800350 """
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000351 self._git_event_log = git_event_log
Raman Tenneti784e16f2021-06-11 17:29:45 -0700352 commit_ids_result = self._GetAllProjectsCommitIds()
353 commit_ids = commit_ids_result.commit_ids
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800354 if not commit_ids:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700355 return UpdateProjectsResult(None, commit_ids_result.fatal)
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800356
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800357 projects_missing_commit_ids = []
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800358 for project in projects:
Raman Tenneti784e16f2021-06-11 17:29:45 -0700359 if self._SkipUpdatingProjectRevisionId(project):
360 continue
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800361 path = project.relpath
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800362 commit_id = commit_ids.get(path)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700363 if not commit_id:
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800364 projects_missing_commit_ids.append(path)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700365
366 # If superproject doesn't have a commit id for a project, then report an
367 # error event and continue as if do not use superproject is specified.
Raman Tenneti21dce3d2021-02-09 00:26:31 -0800368 if projects_missing_commit_ids:
Joanna Wang016a2542023-02-01 15:15:00 -0500369 self._LogWarning('please file a bug using {} to report missing '
370 'commit_ids for: {}', self._manifest.contactinfo.bugurl,
371 projects_missing_commit_ids)
Raman Tenneti784e16f2021-06-11 17:29:45 -0700372 return UpdateProjectsResult(None, False)
373
374 for project in projects:
375 if not self._SkipUpdatingProjectRevisionId(project):
376 project.SetRevisionId(commit_ids.get(project.relpath))
Raman Tenneti1fd7bc22021-02-04 14:39:38 -0800377
Raman Tennetib55769a2021-08-13 11:47:24 -0700378 manifest_path = self._WriteManifestFile()
Raman Tenneti784e16f2021-06-11 17:29:45 -0700379 return UpdateProjectsResult(manifest_path, False)
Xin Li0cb6e922021-06-16 10:19:00 -0700380
381
LaMont Jones2cc3ab72022-04-13 15:58:58 +0000382@functools.lru_cache(maxsize=10)
383def _PrintBetaNotice():
384 """Print the notice of beta status."""
385 print('NOTICE: --use-superproject is in beta; report any issues to the '
386 'address described in `repo version`', file=sys.stderr)
387
388
Xin Li0cb6e922021-06-16 10:19:00 -0700389@functools.lru_cache(maxsize=None)
390def _UseSuperprojectFromConfiguration():
391 """Returns the user choice of whether to use superproject."""
392 user_cfg = RepoConfig.ForUser()
Xin Li0cb6e922021-06-16 10:19:00 -0700393 time_now = int(time.time())
394
395 user_value = user_cfg.GetBoolean('repo.superprojectChoice')
396 if user_value is not None:
397 user_expiration = user_cfg.GetInt('repo.superprojectChoiceExpire')
Xin Li0ec20292021-09-14 16:42:37 -0700398 if user_expiration is None or user_expiration <= 0 or user_expiration >= time_now:
Xin Li0cb6e922021-06-16 10:19:00 -0700399 # TODO(b/190688390) - Remove prompt when we are comfortable with the new
400 # default value.
Xin Li1328c352021-09-08 00:25:30 -0700401 if user_value:
402 print(('You are currently enrolled in Git submodules experiment '
403 '(go/android-submodules-quickstart). Use --no-use-superproject '
404 'to override.\n'), file=sys.stderr)
405 else:
406 print(('You are not currently enrolled in Git submodules experiment '
407 '(go/android-submodules-quickstart). Use --use-superproject '
408 'to override.\n'), file=sys.stderr)
Xin Li6f8c1bf2021-09-24 02:15:39 +0000409 return user_value
Xin Li0cb6e922021-06-16 10:19:00 -0700410
411 # We don't have an unexpired choice, ask for one.
Raman Tennetib55769a2021-08-13 11:47:24 -0700412 system_cfg = RepoConfig.ForSystem()
Xin Li0cb6e922021-06-16 10:19:00 -0700413 system_value = system_cfg.GetBoolean('repo.superprojectChoice')
414 if system_value:
415 # The system configuration is proposing that we should enable the
Xin Li0ec20292021-09-14 16:42:37 -0700416 # use of superproject. Treat the user as enrolled for two weeks.
Xin Li0cb6e922021-06-16 10:19:00 -0700417 #
418 # TODO(b/190688390) - Remove prompt when we are comfortable with the new
419 # default value.
Xin Li0ec20292021-09-14 16:42:37 -0700420 userchoice = True
421 time_choiceexpire = time_now + (86400 * 14)
422 user_cfg.SetString('repo.superprojectChoiceExpire', str(time_choiceexpire))
423 user_cfg.SetBoolean('repo.superprojectChoice', userchoice)
424 print('You are automatically enrolled in Git submodules experiment '
425 '(go/android-submodules-quickstart) for another two weeks.\n',
426 file=sys.stderr)
427 return True
Xin Li0cb6e922021-06-16 10:19:00 -0700428
429 # For all other cases, we would not use superproject by default.
430 return False
431
432
LaMont Jones5fa912b2022-04-14 14:41:13 +0000433def PrintMessages(use_superproject, manifest):
434 """Returns a boolean if error/warning messages are to be printed.
435
436 Args:
437 use_superproject: option value from optparse.
438 manifest: manifest to use.
439 """
440 return use_superproject is not None or bool(manifest.superproject)
Raman Tennetib55769a2021-08-13 11:47:24 -0700441
442
LaMont Jones5fa912b2022-04-14 14:41:13 +0000443def UseSuperproject(use_superproject, manifest):
444 """Returns a boolean if use-superproject option is enabled.
Xin Li0cb6e922021-06-16 10:19:00 -0700445
LaMont Jones5fa912b2022-04-14 14:41:13 +0000446 Args:
447 use_superproject: option value from optparse.
448 manifest: manifest to use.
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000449
450 Returns:
451 Whether the superproject should be used.
LaMont Jones5fa912b2022-04-14 14:41:13 +0000452 """
453
LaMont Jonesff6b1da2022-06-01 21:03:34 +0000454 if not manifest.superproject:
455 # This (sub) manifest does not have a superproject definition.
456 return False
457 elif use_superproject is not None:
LaMont Jones5fa912b2022-04-14 14:41:13 +0000458 return use_superproject
Xin Li0cb6e922021-06-16 10:19:00 -0700459 else:
LaMont Jonesd82be3e2022-04-05 19:30:46 +0000460 client_value = manifest.manifestProject.use_superproject
Xin Li0cb6e922021-06-16 10:19:00 -0700461 if client_value is not None:
462 return client_value
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000463 elif manifest.superproject:
Xin Li0cb6e922021-06-16 10:19:00 -0700464 return _UseSuperprojectFromConfiguration()
LaMont Jonesd56e2eb2022-04-07 18:14:46 +0000465 else:
466 return False