mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-11-09 00:40:11 +01:00
Merge branch 'support-media-files' into 'master'
Support media files This is the first step in supporting adding artibitrary files to fdroid repos, targeted add supporting media files and flashable _update.zip_ files like used to install the Privileged Extension. This reuses the existing metadata fields to keep compatibilty with older versions, but that means that lots of names are confusingly named since they refer to aspects of an APK rather than a generic file. We can address that later when we refactor the whole index metadata. See merge request !177
This commit is contained in:
commit
7dde16dd11
@ -1609,6 +1609,12 @@ class KnownApks:
|
|||||||
return lst
|
return lst
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_extension(filename):
|
||||||
|
"""get the normalized file extension, can be blank string but never None"""
|
||||||
|
|
||||||
|
return os.path.splitext(filename)[1].lower()[1:]
|
||||||
|
|
||||||
|
|
||||||
def isApkDebuggable(apkfile, config):
|
def isApkDebuggable(apkfile, config):
|
||||||
"""Returns True if the given apk file is debuggable
|
"""Returns True if the given apk file is debuggable
|
||||||
|
|
||||||
|
@ -504,12 +504,82 @@ def insert_obbs(repodir, apps, apks):
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def scan_apks(apps, apkcache, repodir, knownapks, use_date_from_apk=False):
|
def scan_repo_files(apkcache, repodir, knownapks, use_date_from_file=False):
|
||||||
|
"""Scan a repo for all files with an extension except APK/OBB
|
||||||
|
|
||||||
|
:param apkcache: current cached info about all repo files
|
||||||
|
:param repodir: repo directory to scan
|
||||||
|
:param knownapks: list of all known files, as per metadata.read_metadata
|
||||||
|
:param use_date_from_file: use date from file (instead of current date)
|
||||||
|
for newly added files
|
||||||
|
"""
|
||||||
|
|
||||||
|
cachechanged = False
|
||||||
|
repo_files = []
|
||||||
|
for name in os.listdir(repodir):
|
||||||
|
if name in ['index.jar', 'index.xml', 'index.html', 'categories.txt', ]:
|
||||||
|
continue
|
||||||
|
file_extension = common.get_file_extension(name)
|
||||||
|
if file_extension == 'apk' or file_extension == 'obb':
|
||||||
|
continue
|
||||||
|
filename = os.path.join(repodir, name)
|
||||||
|
if not os.path.isfile(filename):
|
||||||
|
continue
|
||||||
|
stat = os.stat(filename)
|
||||||
|
if stat.st_size == 0:
|
||||||
|
logging.error(filename + ' is zero size!')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
shasum = sha256sum(filename)
|
||||||
|
usecache = False
|
||||||
|
if name in apkcache:
|
||||||
|
repo_file = apkcache[name]
|
||||||
|
if repo_file['sha256'] == shasum:
|
||||||
|
logging.debug("Reading " + name + " from cache")
|
||||||
|
usecache = True
|
||||||
|
else:
|
||||||
|
logging.debug("Ignoring stale cache data for " + name)
|
||||||
|
elif not usecache:
|
||||||
|
logging.debug("Processing " + name)
|
||||||
|
repo_file = {}
|
||||||
|
# TODO rename apkname globally to something more generic
|
||||||
|
repo_file['name'] = name
|
||||||
|
repo_file['apkname'] = name
|
||||||
|
repo_file['sha256'] = shasum
|
||||||
|
repo_file['versioncode'] = 0
|
||||||
|
repo_file['version'] = shasum
|
||||||
|
# the static ID is the SHA256 unless it is set in the metadata
|
||||||
|
repo_file['id'] = shasum
|
||||||
|
srcfilename = name + ".src.tar.gz"
|
||||||
|
if os.path.exists(os.path.join(repodir, srcfilename)):
|
||||||
|
repo_file['srcname'] = srcfilename
|
||||||
|
repo_file['size'] = stat.st_size
|
||||||
|
|
||||||
|
apkcache[name] = repo_file
|
||||||
|
cachechanged = True
|
||||||
|
|
||||||
|
if use_date_from_file:
|
||||||
|
timestamp = stat.st_ctime
|
||||||
|
default_date_param = datetime.fromtimestamp(timestamp).utctimetuple()
|
||||||
|
else:
|
||||||
|
default_date_param = None
|
||||||
|
|
||||||
|
# Record in knownapks, getting the added date at the same time..
|
||||||
|
added = knownapks.recordapk(repo_file['apkname'], repo_file['id'],
|
||||||
|
default_date=default_date_param)
|
||||||
|
if added:
|
||||||
|
repo_file['added'] = added
|
||||||
|
|
||||||
|
repo_files.append(repo_file)
|
||||||
|
|
||||||
|
return repo_files, cachechanged
|
||||||
|
|
||||||
|
|
||||||
|
def scan_apks(apkcache, repodir, knownapks, use_date_from_apk=False):
|
||||||
"""Scan the apks in the given repo directory.
|
"""Scan the apks in the given repo directory.
|
||||||
|
|
||||||
This also extracts the icons.
|
This also extracts the icons.
|
||||||
|
|
||||||
:param apps: list of all applications, as per metadata.read_metadata
|
|
||||||
:param apkcache: current apk cache information
|
:param apkcache: current apk cache information
|
||||||
:param repodir: repo directory to scan
|
:param repodir: repo directory to scan
|
||||||
:param knownapks: known apks info
|
:param knownapks: known apks info
|
||||||
@ -884,6 +954,12 @@ def make_index(apps, sortedids, apks, repodir, archive, categories):
|
|||||||
return
|
return
|
||||||
addElement(name, value, doc, parent)
|
addElement(name, value, doc, parent)
|
||||||
|
|
||||||
|
def addElementIfInApk(name, apk, key, doc, parent):
|
||||||
|
if key not in apk:
|
||||||
|
return
|
||||||
|
value = str(apk[key])
|
||||||
|
addElement(name, value, doc, parent)
|
||||||
|
|
||||||
def addElementCDATA(name, value, doc, parent):
|
def addElementCDATA(name, value, doc, parent):
|
||||||
el = doc.createElement(name)
|
el = doc.createElement(name)
|
||||||
el.appendChild(doc.createCDATASection(value))
|
el.appendChild(doc.createCDATASection(value))
|
||||||
@ -1059,6 +1135,7 @@ def make_index(apps, sortedids, apks, repodir, archive, categories):
|
|||||||
current_version_code = 0
|
current_version_code = 0
|
||||||
current_version_file = None
|
current_version_file = None
|
||||||
for apk in apklist:
|
for apk in apklist:
|
||||||
|
file_extension = common.get_file_extension(apk['apkname'])
|
||||||
# find the APK for the "Current Version"
|
# find the APK for the "Current Version"
|
||||||
if current_version_code < apk['versioncode']:
|
if current_version_code < apk['versioncode']:
|
||||||
current_version_code = apk['versioncode']
|
current_version_code = apk['versioncode']
|
||||||
@ -1070,8 +1147,7 @@ def make_index(apps, sortedids, apks, repodir, archive, categories):
|
|||||||
addElement('version', apk['version'], doc, apkel)
|
addElement('version', apk['version'], doc, apkel)
|
||||||
addElement('versioncode', str(apk['versioncode']), doc, apkel)
|
addElement('versioncode', str(apk['versioncode']), doc, apkel)
|
||||||
addElement('apkname', apk['apkname'], doc, apkel)
|
addElement('apkname', apk['apkname'], doc, apkel)
|
||||||
if 'srcname' in apk:
|
addElementIfInApk('srcname', apk, 'srcname', doc, apkel)
|
||||||
addElement('srcname', apk['srcname'], doc, apkel)
|
|
||||||
for hash_type in ['sha256']:
|
for hash_type in ['sha256']:
|
||||||
if hash_type not in apk:
|
if hash_type not in apk:
|
||||||
continue
|
continue
|
||||||
@ -1079,44 +1155,49 @@ def make_index(apps, sortedids, apks, repodir, archive, categories):
|
|||||||
hashel.setAttribute("type", hash_type)
|
hashel.setAttribute("type", hash_type)
|
||||||
hashel.appendChild(doc.createTextNode(apk[hash_type]))
|
hashel.appendChild(doc.createTextNode(apk[hash_type]))
|
||||||
apkel.appendChild(hashel)
|
apkel.appendChild(hashel)
|
||||||
addElement('sig', apk['sig'], doc, apkel)
|
|
||||||
addElement('size', str(apk['size']), doc, apkel)
|
addElement('size', str(apk['size']), doc, apkel)
|
||||||
addElement('sdkver', str(apk['minSdkVersion']), doc, apkel)
|
addElement('sdkver', str(apk['minSdkVersion']), doc, apkel)
|
||||||
if 'targetSdkVersion' in apk:
|
addElementIfInApk('targetSdkVersion', apk,
|
||||||
addElement('targetSdkVersion', str(apk['targetSdkVersion']), doc, apkel)
|
'targetSdkVersion', doc, apkel)
|
||||||
if 'maxSdkVersion' in apk:
|
addElementIfInApk('maxsdkver', apk,
|
||||||
addElement('maxsdkver', str(apk['maxSdkVersion']), doc, apkel)
|
'maxSdkVersion', doc, apkel)
|
||||||
addElementNonEmpty('obbMainFile', apk.get('obbMainFile'), doc, apkel)
|
addElementIfInApk('obbMainFile', apk,
|
||||||
addElementNonEmpty('obbMainFileSha256', apk.get('obbMainFileSha256'), doc, apkel)
|
'obbMainFile', doc, apkel)
|
||||||
addElementNonEmpty('obbPatchFile', apk.get('obbPatchFile'), doc, apkel)
|
addElementIfInApk('obbMainFileSha256', apk,
|
||||||
addElementNonEmpty('obbPatchFileSha256', apk.get('obbPatchFileSha256'), doc, apkel)
|
'obbMainFileSha256', doc, apkel)
|
||||||
|
addElementIfInApk('obbPatchFile', apk,
|
||||||
|
'obbPatchFile', doc, apkel)
|
||||||
|
addElementIfInApk('obbPatchFileSha256', apk,
|
||||||
|
'obbPatchFileSha256', doc, apkel)
|
||||||
if 'added' in apk:
|
if 'added' in apk:
|
||||||
addElement('added', time.strftime('%Y-%m-%d', apk['added']), doc, apkel)
|
addElement('added', time.strftime('%Y-%m-%d', apk['added']), doc, apkel)
|
||||||
|
|
||||||
# TODO: remove old permission format
|
if file_extension == 'apk': # sig is required for APKs, but only APKs
|
||||||
old_permissions = set()
|
addElement('sig', apk['sig'], doc, apkel)
|
||||||
for perm in apk['uses-permission']:
|
|
||||||
perm_name = perm.name
|
|
||||||
if perm_name.startswith("android.permission."):
|
|
||||||
perm_name = perm_name[19:]
|
|
||||||
old_permissions.add(perm_name)
|
|
||||||
addElementNonEmpty('permissions', ','.join(old_permissions), doc, apkel)
|
|
||||||
|
|
||||||
for permission in apk['uses-permission']:
|
old_permissions = set()
|
||||||
permel = doc.createElement('uses-permission')
|
for perm in apk['uses-permission']:
|
||||||
permel.setAttribute('name', permission.name)
|
perm_name = perm.name
|
||||||
if permission.maxSdkVersion is not None:
|
if perm_name.startswith("android.permission."):
|
||||||
permel.setAttribute('maxSdkVersion', permission.maxSdkVersion)
|
perm_name = perm_name[19:]
|
||||||
apkel.appendChild(permel)
|
old_permissions.add(perm_name)
|
||||||
for permission_sdk_23 in apk['uses-permission-sdk-23']:
|
addElementNonEmpty('permissions', ','.join(old_permissions), doc, apkel)
|
||||||
permel = doc.createElement('uses-permission-sdk-23')
|
|
||||||
permel.setAttribute('name', permission_sdk_23.name)
|
for permission in apk['uses-permission']:
|
||||||
if permission_sdk_23.maxSdkVersion is not None:
|
permel = doc.createElement('uses-permission')
|
||||||
permel.setAttribute('maxSdkVersion', permission_sdk_23.maxSdkVersion)
|
permel.setAttribute('name', permission.name)
|
||||||
apkel.appendChild(permel)
|
if permission.maxSdkVersion is not None:
|
||||||
if 'nativecode' in apk:
|
permel.setAttribute('maxSdkVersion', permission.maxSdkVersion)
|
||||||
addElement('nativecode', ','.join(apk['nativecode']), doc, apkel)
|
apkel.appendChild(permel)
|
||||||
addElementNonEmpty('features', ','.join(apk['features']), doc, apkel)
|
for permission_sdk_23 in apk['uses-permission-sdk-23']:
|
||||||
|
permel = doc.createElement('uses-permission-sdk-23')
|
||||||
|
permel.setAttribute('name', permission_sdk_23.name)
|
||||||
|
if permission_sdk_23.maxSdkVersion is not None:
|
||||||
|
permel.setAttribute('maxSdkVersion', permission_sdk_23.maxSdkVersion)
|
||||||
|
apkel.appendChild(permel)
|
||||||
|
if 'nativecode' in apk:
|
||||||
|
addElement('nativecode', ','.join(apk['nativecode']), doc, apkel)
|
||||||
|
addElementNonEmpty('features', ','.join(apk['features']), doc, apkel)
|
||||||
|
|
||||||
if current_version_file is not None \
|
if current_version_file is not None \
|
||||||
and config['make_current_version_link'] \
|
and config['make_current_version_link'] \
|
||||||
@ -1394,8 +1475,12 @@ def main():
|
|||||||
delete_disabled_builds(apps, apkcache, repodirs)
|
delete_disabled_builds(apps, apkcache, repodirs)
|
||||||
|
|
||||||
# Scan all apks in the main repo
|
# Scan all apks in the main repo
|
||||||
apks, cachechanged = scan_apks(apps, apkcache, repodirs[0], knownapks, options.use_date_from_apk)
|
apks, cachechanged = scan_apks(apkcache, repodirs[0], knownapks, options.use_date_from_apk)
|
||||||
|
|
||||||
|
files, fcachechanged = scan_repo_files(apkcache, repodirs[0], knownapks,
|
||||||
|
options.use_date_from_apk)
|
||||||
|
cachechanged = cachechanged or fcachechanged
|
||||||
|
apks += files
|
||||||
# Generate warnings for apk's with no metadata (or create skeleton
|
# Generate warnings for apk's with no metadata (or create skeleton
|
||||||
# metadata files, if requested on the command line)
|
# metadata files, if requested on the command line)
|
||||||
newmetadata = False
|
newmetadata = False
|
||||||
@ -1438,18 +1523,21 @@ def main():
|
|||||||
|
|
||||||
# Scan the archive repo for apks as well
|
# Scan the archive repo for apks as well
|
||||||
if len(repodirs) > 1:
|
if len(repodirs) > 1:
|
||||||
archapks, cc = scan_apks(apps, apkcache, repodirs[1], knownapks, options.use_date_from_apk)
|
archapks, cc = scan_apks(apkcache, repodirs[1], knownapks, options.use_date_from_apk)
|
||||||
if cc:
|
if cc:
|
||||||
cachechanged = True
|
cachechanged = True
|
||||||
else:
|
else:
|
||||||
archapks = []
|
archapks = []
|
||||||
|
|
||||||
|
# less than the valid range of versionCode, i.e. Java's Integer.MIN_VALUE
|
||||||
|
UNSET_VERSION_CODE = -0x100000000
|
||||||
|
|
||||||
# Some information from the apks needs to be applied up to the application
|
# Some information from the apks needs to be applied up to the application
|
||||||
# level. When doing this, we use the info from the most recent version's apk.
|
# level. When doing this, we use the info from the most recent version's apk.
|
||||||
# We deal with figuring out when the app was added and last updated at the
|
# We deal with figuring out when the app was added and last updated at the
|
||||||
# same time.
|
# same time.
|
||||||
for appid, app in apps.items():
|
for appid, app in apps.items():
|
||||||
bestver = 0
|
bestver = UNSET_VERSION_CODE
|
||||||
for apk in apks + archapks:
|
for apk in apks + archapks:
|
||||||
if apk['id'] == appid:
|
if apk['id'] == appid:
|
||||||
if apk['versioncode'] > bestver:
|
if apk['versioncode'] > bestver:
|
||||||
@ -1467,7 +1555,7 @@ def main():
|
|||||||
if not app.lastupdated:
|
if not app.lastupdated:
|
||||||
logging.debug("Don't know when " + appid + " was last updated")
|
logging.debug("Don't know when " + appid + " was last updated")
|
||||||
|
|
||||||
if bestver == 0:
|
if bestver == UNSET_VERSION_CODE:
|
||||||
if app.Name is None:
|
if app.Name is None:
|
||||||
app.Name = app.AutoName or appid
|
app.Name = app.AutoName or appid
|
||||||
app.icon = None
|
app.icon = None
|
||||||
|
@ -101,7 +101,7 @@ class UpdateTest(unittest.TestCase):
|
|||||||
|
|
||||||
apps = fdroidserver.metadata.read_metadata(xref=True)
|
apps = fdroidserver.metadata.read_metadata(xref=True)
|
||||||
knownapks = fdroidserver.common.KnownApks()
|
knownapks = fdroidserver.common.KnownApks()
|
||||||
apks, cachechanged = fdroidserver.update.scan_apks(apps, {}, 'repo', knownapks, False)
|
apks, cachechanged = fdroidserver.update.scan_apks({}, 'repo', knownapks, False)
|
||||||
self.assertEqual(len(apks), 6)
|
self.assertEqual(len(apks), 6)
|
||||||
apk = apks[0]
|
apk = apks[0]
|
||||||
self.assertEqual(apk['minSdkVersion'], '4')
|
self.assertEqual(apk['minSdkVersion'], '4')
|
||||||
|
Loading…
Reference in New Issue
Block a user