blob: 13f9db6a3825f7773e604b31b0fc5c5b37ab124b [file] [log] [blame]
Eric Parke4e0a352020-06-19 12:06:29 -04001#!/usr/bin/env python
2# Copyright (C) 2012-2013, The CyanogenMod Project
3# (C) 2017, The LineageOS Project
Michael Bestas3952f6c2016-08-26 01:12:08 +03004#
Eric Parke4e0a352020-06-19 12:06:29 -04005# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
Michael Bestas3952f6c2016-08-26 01:12:08 +03008#
Eric Parke4e0a352020-06-19 12:06:29 -04009# http://www.apache.org/licenses/LICENSE-2.0
Michael Bestas3952f6c2016-08-26 01:12:08 +030010#
Eric Parke4e0a352020-06-19 12:06:29 -040011# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
Michael Bestas3952f6c2016-08-26 01:12:08 +030016
17from __future__ import print_function
Eric Parke4e0a352020-06-19 12:06:29 -040018
19import base64
Michael Bestas3952f6c2016-08-26 01:12:08 +030020import json
Eric Parke4e0a352020-06-19 12:06:29 -040021import netrc
Michael Bestas3952f6c2016-08-26 01:12:08 +030022import os
23import re
Eric Parke4e0a352020-06-19 12:06:29 -040024import sys
Michael Bestas3952f6c2016-08-26 01:12:08 +030025try:
Eric Parke4e0a352020-06-19 12:06:29 -040026 # For python3
27 import urllib.error
28 import urllib.parse
29 import urllib.request
Michael Bestas3952f6c2016-08-26 01:12:08 +030030except ImportError:
Eric Parke4e0a352020-06-19 12:06:29 -040031 # For python2
32 import imp
33 import urllib2
34 import urlparse
35 urllib = imp.new_module('urllib')
36 urllib.error = urllib2
37 urllib.parse = urlparse
38 urllib.request = urllib2
Michael Bestas3952f6c2016-08-26 01:12:08 +030039
Eric Parke4e0a352020-06-19 12:06:29 -040040from xml.etree import ElementTree
Michael Bestas3952f6c2016-08-26 01:12:08 +030041
Eric Parke4e0a352020-06-19 12:06:29 -040042product = sys.argv[1]
Michael Bestas3952f6c2016-08-26 01:12:08 +030043
Eric Parke4e0a352020-06-19 12:06:29 -040044if len(sys.argv) > 2:
45 depsonly = sys.argv[2]
46else:
47 depsonly = None
Michael Bestas3952f6c2016-08-26 01:12:08 +030048
Eric Parke4e0a352020-06-19 12:06:29 -040049try:
50 device = product[product.index("_") + 1:]
51except:
52 device = product
Michael Bestas3952f6c2016-08-26 01:12:08 +030053
Eric Parke4e0a352020-06-19 12:06:29 -040054if not depsonly:
Eric Park128ab0c2020-06-19 12:53:06 -040055 print("Device %s not found. Attempting to retrieve device repository from BlissRoms-Devices GitHub (http://github.com/BlissRoms-Devices)." % device)
Eric Parke4e0a352020-06-19 12:06:29 -040056
57repositories = []
58
59try:
60 authtuple = netrc.netrc().authenticators("api.github.com")
61
62 if authtuple:
63 auth_string = ('%s:%s' % (authtuple[0], authtuple[2])).encode()
64 githubauth = base64.encodestring(auth_string).decode().replace('\n', '')
65 else:
66 githubauth = None
67except:
68 githubauth = None
69
70def add_auth(githubreq):
71 if githubauth:
72 githubreq.add_header("Authorization","Basic %s" % githubauth)
73
74if not depsonly:
Eric Park128ab0c2020-06-19 12:53:06 -040075 githubreq = urllib.request.Request("https://api.github.com/search/repositories?q=%s+user:BlissRoms-Devices+in:name+fork:true" % device)
Eric Parke4e0a352020-06-19 12:06:29 -040076 add_auth(githubreq)
Michael Bestas3952f6c2016-08-26 01:12:08 +030077 try:
Eric Parke4e0a352020-06-19 12:06:29 -040078 result = json.loads(urllib.request.urlopen(githubreq).read().decode())
79 except urllib.error.URLError:
80 print("Failed to search GitHub")
81 sys.exit()
82 except ValueError:
83 print("Failed to parse return data from GitHub")
84 sys.exit()
85 for res in result.get('items', []):
86 repositories.append(res)
Michael Bestas3952f6c2016-08-26 01:12:08 +030087
Eric Parke4e0a352020-06-19 12:06:29 -040088local_manifests = r'.repo/local_manifests'
89if not os.path.exists(local_manifests): os.makedirs(local_manifests)
Michael Bestas3952f6c2016-08-26 01:12:08 +030090
Eric Parke4e0a352020-06-19 12:06:29 -040091def exists_in_tree(lm, path):
92 for child in lm.getchildren():
93 if child.attrib['path'] == path:
Michael Bestas3952f6c2016-08-26 01:12:08 +030094 return True
95 return False
96
Eric Parke4e0a352020-06-19 12:06:29 -040097# in-place prettyprint formatter
Michael Bestas3952f6c2016-08-26 01:12:08 +030098def indent(elem, level=0):
Eric Parke4e0a352020-06-19 12:06:29 -040099 i = "\n" + level*" "
Michael Bestas3952f6c2016-08-26 01:12:08 +0300100 if len(elem):
101 if not elem.text or not elem.text.strip():
Eric Parke4e0a352020-06-19 12:06:29 -0400102 elem.text = i + " "
Michael Bestas3952f6c2016-08-26 01:12:08 +0300103 if not elem.tail or not elem.tail.strip():
104 elem.tail = i
105 for elem in elem:
106 indent(elem, level+1)
107 if not elem.tail or not elem.tail.strip():
108 elem.tail = i
109 else:
110 if level and (not elem.tail or not elem.tail.strip()):
111 elem.tail = i
112
Tom Powell93715642020-02-25 20:45:43 -0800113def get_manifest_path():
114 '''Find the current manifest path
115 In old versions of repo this is at .repo/manifest.xml
116 In new versions, .repo/manifest.xml includes an include
117 to some arbitrary file in .repo/manifests'''
118
Eric Parke4e0a352020-06-19 12:06:29 -0400119 m = ElementTree.parse(".repo/manifest.xml")
Tom Powell93715642020-02-25 20:45:43 -0800120 try:
121 m.findall('default')[0]
122 return '.repo/manifest.xml'
123 except IndexError:
124 return ".repo/manifests/{}".format(m.find("include").get("name"))
125
126def get_default_revision():
Eric Park128ab0c2020-06-19 12:53:06 -0400127 #m = ElementTree.parse(get_manifest_path())
128 #d = m.findall('default')[0]
129 #r = d.get('revision')
130 #return r.replace('refs/heads/', '').replace('refs/tags/', '')
131 # Return q branch for now (workaround)
132 return "q"
Michael Bestas3952f6c2016-08-26 01:12:08 +0300133
Eric Parke4e0a352020-06-19 12:06:29 -0400134def get_from_manifest(devicename):
Michael Bestas3952f6c2016-08-26 01:12:08 +0300135 try:
Eric Parke4e0a352020-06-19 12:06:29 -0400136 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
Michael Bestas3952f6c2016-08-26 01:12:08 +0300137 lm = lm.getroot()
Eric Parke4e0a352020-06-19 12:06:29 -0400138 except:
139 lm = ElementTree.Element("manifest")
Michael Bestas3952f6c2016-08-26 01:12:08 +0300140
Eric Parke4e0a352020-06-19 12:06:29 -0400141 for localpath in lm.findall("project"):
142 if re.search("android_device_.*_%s$" % device, localpath.get("name")):
143 return localpath.get("path")
Michael Bestas3952f6c2016-08-26 01:12:08 +0300144
Michael Bestas3952f6c2016-08-26 01:12:08 +0300145 return None
146
Eric Park88540fa2020-06-19 13:23:15 -0400147def project_is_removed_device(projectpath):
148 try:
149 lm = ElementTree.parse(".repo/manifests/bliss.xml")
150 lm = lm.getroot()
151 except:
152 lm = ElementTree.Element("manifest")
153 for localpath in lm.findall("remove-project"):
154 if localpath.get("name") == projectpath:
155 print("Warning: project was removed from AOSP manifest")
156 return True
157
158
Eric Parke4e0a352020-06-19 12:06:29 -0400159def is_in_manifest(projectpath):
Jackeagle76312042019-10-13 05:33:32 -0400160 try:
Eric Parke4e0a352020-06-19 12:06:29 -0400161 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
162 lm = lm.getroot()
163 except:
164 lm = ElementTree.Element("manifest")
Michael Bestas3952f6c2016-08-26 01:12:08 +0300165
Eric Parke4e0a352020-06-19 12:06:29 -0400166 for localpath in lm.findall("project"):
167 if localpath.get("path") == projectpath:
168 return True
Michael Bestas3952f6c2016-08-26 01:12:08 +0300169
Eric Parke4e0a352020-06-19 12:06:29 -0400170 # Search in main manifest, too
Jackeagle76312042019-10-13 05:33:32 -0400171 try:
Tom Powell93715642020-02-25 20:45:43 -0800172 lm = ElementTree.parse(get_manifest_path())
Eric Parke4e0a352020-06-19 12:06:29 -0400173 lm = lm.getroot()
174 except:
175 lm = ElementTree.Element("manifest")
Jackeagle76312042019-10-13 05:33:32 -0400176
Eric Parke4e0a352020-06-19 12:06:29 -0400177 for localpath in lm.findall("project"):
178 if localpath.get("path") == projectpath:
Eric Park88540fa2020-06-19 13:23:15 -0400179 # Detect whether proejctpath is a device or not
180 if project_is_removed_device(projectpath):
181 return False
Eric Parke4e0a352020-06-19 12:06:29 -0400182 return True
183
Eric Park128ab0c2020-06-19 12:53:06 -0400184 # ... and don't forget the bliss snippet
Eric Parke4e0a352020-06-19 12:06:29 -0400185 try:
Eric Park128ab0c2020-06-19 12:53:06 -0400186 lm = ElementTree.parse(".repo/manifests/bliss.xml")
Eric Parke4e0a352020-06-19 12:06:29 -0400187 lm = lm.getroot()
188 except:
189 lm = ElementTree.Element("manifest")
190
191 for localpath in lm.findall("project"):
192 if localpath.get("path") == projectpath:
193 return True
194
195 return False
196
197def add_to_manifest(repositories, fallback_branch = None):
198 try:
199 lm = ElementTree.parse(".repo/local_manifests/roomservice.xml")
200 lm = lm.getroot()
201 except:
202 lm = ElementTree.Element("manifest")
203
204 for repository in repositories:
205 repo_name = repository['repository']
206 repo_target = repository['target_path']
207 print('Checking if %s is fetched from %s' % (repo_target, repo_name))
208 if is_in_manifest(repo_target):
Eric Park128ab0c2020-06-19 12:53:06 -0400209 print('BlissRoms/%s already fetched to %s' % (repo_name, repo_target))
Eric Parke4e0a352020-06-19 12:06:29 -0400210 continue
211
Eric Park128ab0c2020-06-19 12:53:06 -0400212 print('Adding dependency: %s -> %s' % (repo_name, repo_target))
213
214 # Check if repository name contains organization
215 if "/" in repo_name:
216 project = ElementTree.Element("project", attrib = { "path": repo_target,
217 "remote": "github", "name": "%s" % repo_name })
218 else:
219 project = ElementTree.Element("project", attrib = { "path": repo_target,
220 "remote": "github", "name": "BlissRoms-Devices/%s" % repo_name })
Eric Parke4e0a352020-06-19 12:06:29 -0400221
Eric Parkfa8ffd02020-06-19 13:57:06 -0400222 if 'revision' in repository:
223 project.set('revision',repository['revision'])
Eric Parke4e0a352020-06-19 12:06:29 -0400224 elif fallback_branch:
225 print("Using fallback branch %s for %s" % (fallback_branch, repo_name))
226 project.set('revision', fallback_branch)
227 else:
228 print("Using default branch for %s" % repo_name)
Eric Park128ab0c2020-06-19 12:53:06 -0400229 project.set('revision', "q")
Eric Parke4e0a352020-06-19 12:06:29 -0400230
231 lm.append(project)
232
233 indent(lm, 0)
234 raw_xml = ElementTree.tostring(lm).decode()
235 raw_xml = '<?xml version="1.0" encoding="UTF-8"?>\n' + raw_xml
236
237 f = open('.repo/local_manifests/roomservice.xml', 'w')
238 f.write(raw_xml)
239 f.close()
240
241def fetch_dependencies(repo_path, fallback_branch = None):
242 print('Looking for dependencies in %s' % repo_path)
Eric Park128ab0c2020-06-19 12:53:06 -0400243 dependencies_path = repo_path + '/bliss.dependencies'
Eric Parke4e0a352020-06-19 12:06:29 -0400244 syncable_repos = []
245 verify_repos = []
246
247 if os.path.exists(dependencies_path):
248 dependencies_file = open(dependencies_path, 'r')
249 dependencies = json.loads(dependencies_file.read())
250 fetch_list = []
251
252 for dependency in dependencies:
253 if not is_in_manifest(dependency['target_path']):
254 fetch_list.append(dependency)
255 syncable_repos.append(dependency['target_path'])
256 verify_repos.append(dependency['target_path'])
257 else:
258 verify_repos.append(dependency['target_path'])
259
260 dependencies_file.close()
261
262 if len(fetch_list) > 0:
263 print('Adding dependencies to manifest')
264 add_to_manifest(fetch_list, fallback_branch)
Michael Bestas3952f6c2016-08-26 01:12:08 +0300265 else:
Eric Parke4e0a352020-06-19 12:06:29 -0400266 print('%s has no additional dependencies.' % repo_path)
Michael Bestas3952f6c2016-08-26 01:12:08 +0300267
Eric Parke4e0a352020-06-19 12:06:29 -0400268 if len(syncable_repos) > 0:
269 print('Syncing dependencies')
270 os.system('repo sync --force-sync %s' % ' '.join(syncable_repos))
271
272 for deprepo in verify_repos:
273 fetch_dependencies(deprepo)
274
275def has_branch(branches, revision):
276 return revision in [branch['name'] for branch in branches]
277
278if depsonly:
279 repo_path = get_from_manifest(device)
280 if repo_path:
281 fetch_dependencies(repo_path)
282 else:
283 print("Trying dependencies-only mode on a non-existing device tree?")
284
285 sys.exit()
286
287else:
288 for repository in repositories:
289 repo_name = repository['name']
290 if re.match(r"^android_device_[^_]*_" + device + "$", repo_name):
291 print("Found repository: %s" % repository['name'])
Eric Park128ab0c2020-06-19 12:53:06 -0400292
Eric Parke4e0a352020-06-19 12:06:29 -0400293 manufacturer = repo_name.replace("android_device_", "").replace("_" + device, "")
Eric Park128ab0c2020-06-19 12:53:06 -0400294
Eric Parke4e0a352020-06-19 12:06:29 -0400295 default_revision = get_default_revision()
296 print("Default revision: %s" % default_revision)
297 print("Checking branch info")
298 githubreq = urllib.request.Request(repository['branches_url'].replace('{/branch}', ''))
299 add_auth(githubreq)
300 result = json.loads(urllib.request.urlopen(githubreq).read().decode())
301
Eric Park128ab0c2020-06-19 12:53:06 -0400302
Eric Parke4e0a352020-06-19 12:06:29 -0400303 ## Try tags, too, since that's what releases use
304 if not has_branch(result, default_revision):
305 githubreq = urllib.request.Request(repository['tags_url'].replace('{/tag}', ''))
306 add_auth(githubreq)
307 result.extend (json.loads(urllib.request.urlopen(githubreq).read().decode()))
Eric Park128ab0c2020-06-19 12:53:06 -0400308
Eric Parke4e0a352020-06-19 12:06:29 -0400309 repo_path = "device/%s/%s" % (manufacturer, device)
310 adding = {'repository':repo_name,'target_path':repo_path}
Eric Park128ab0c2020-06-19 12:53:06 -0400311
Eric Parke4e0a352020-06-19 12:06:29 -0400312 fallback_branch = None
313 if not has_branch(result, default_revision):
314 if os.getenv('ROOMSERVICE_BRANCHES'):
315 fallbacks = list(filter(bool, os.getenv('ROOMSERVICE_BRANCHES').split(' ')))
316 for fallback in fallbacks:
317 if has_branch(result, fallback):
318 print("Using fallback branch: %s" % fallback)
319 fallback_branch = fallback
320 break
321
322 if not fallback_branch:
323 print("Default revision %s not found in %s. Bailing." % (default_revision, repo_name))
324 print("Branches found:")
325 for branch in [branch['name'] for branch in result]:
326 print(branch)
327 print("Use the ROOMSERVICE_BRANCHES environment variable to specify a list of fallback branches.")
328 sys.exit()
329
330 add_to_manifest([adding], fallback_branch)
331
332 print("Syncing repository to retrieve project.")
333 os.system('repo sync --force-sync %s' % repo_path)
334 print("Repository synced!")
335
336 fetch_dependencies(repo_path, fallback_branch)
337 print("Done")
338 sys.exit()
339
Eric Park128ab0c2020-06-19 12:53:06 -0400340print("Repository for %s not found in the BlissRoms-Devices Github repository list. If this is in error, you may need to manually add it to your local_manifests/roomservice.xml." % device)