1
0
mirror of https://gitlab.com/fdroid/fdroidserver.git synced 2024-11-09 00:40:11 +01:00

Support for publishing signed binaries from elsewhere

Done after verifying that they match ones built using a recipe.
Everything in the metadata should be the same as normal, with the
addition of the Binaries: directive to specify where (with pattern
substitution) to get the binaries from.

Publishing only takes place if there is a proper match. (Which seems
very unlikely to be the case unless the exact same toolchain is used, so
I would imagine that unless the person building and signing the incoming
binaries uses fdroidserver to build them, probably the exact same
buildserver id, they will not match. But at least we have the
functionality to support that.)
This commit is contained in:
Ciaran Gultnieks 2014-10-24 21:04:15 +01:00
parent 311ec604f8
commit 8568805866
4 changed files with 154 additions and 72 deletions

View File

@ -1785,3 +1785,40 @@ def place_srclib(root_dir, number, libpath):
o.write(line)
if not placed:
o.write('android.library.reference.%d=%s\n' % (number, relpath))
def compare_apks(apk1, apk2, tmp_dir):
"""Compare two apks
Returns None if the apk content is the same (apart from the signing key),
otherwise a string describing what's different, or what went wrong when
trying to do the comparison.
"""
thisdir = os.path.join(tmp_dir, 'this_apk')
thatdir = os.path.join(tmp_dir, 'that_apk')
for d in [thisdir, thatdir]:
if os.path.exists(d):
shutil.rmtree(d)
os.mkdir(d)
if subprocess.call(['jar', 'xf',
os.path.abspath(apk1)],
cwd=thisdir) != 0:
return("Failed to unpack " + apk1)
if subprocess.call(['jar', 'xf',
os.path.abspath(apk2)],
cwd=thatdir) != 0:
return("Failed to unpack " + apk2)
p = FDroidPopen(['diff', '-r', 'this_apk', 'that_apk'], cwd=tmp_dir,
output=False)
lines = p.output.splitlines()
if len(lines) != 1 or 'META-INF' not in lines[0]:
return("Unexpected diff output - " + p.output)
# If we get here, it seems like they're the same!
return None

View File

@ -57,6 +57,7 @@ app_defaults = OrderedDict([
('Requires Root', False),
('Repo Type', ''),
('Repo', ''),
('Binaries', ''),
('Maintainer Notes', []),
('Archive Policy', None),
('Auto Update Mode', 'None'),
@ -197,6 +198,11 @@ valuetypes = {
["Repo Type"],
[]),
FieldValidator("Binaries",
r'^http[s]?://', None,
["Binaries"],
[]),
FieldValidator("Archive Policy",
r'^[0-9]+ versions$', None,
["Archive Policy"],
@ -851,6 +857,8 @@ def write_metadata(dest, app):
if app['Repo Type']:
writefield('Repo Type')
writefield('Repo')
if app['Binaries']:
writefield('Binaries')
mf.write('\n')
for build in app['builds']:

View File

@ -111,6 +111,59 @@ def main():
continue
logging.info("Processing " + apkfile)
# There ought to be valid metadata for this app, otherwise why are we
# trying to publish it?
if not appid in allapps:
logging.error("Unexpected {0} found in unsigned directory"
.format(apkfilename))
sys.exit(1)
app = allapps[appid]
if 'Binaries' in app:
# It's an app where we build from source, and verify the apk
# contents against a developer's binary, and then publish their
# version if everything checks out.
# Need the version name for the version code...
versionname = None
for build in app['builds']:
if build['vercode'] == vercode:
versionname = build['version']
break
if not versionname:
logging.error("...no defined build for version code {0}"
.format(vercode))
continue
# Figure out where the developer's binary is supposed to come from...
url = app['Binaries']
url = url.replace('%v', versionname)
url = url.replace('%c', str(vercode))
# Grab the binary from where the developer publishes it...
logging.info("...retrieving " + url)
srcapk = os.path.join(tmp_dir, url.split('/')[-1])
p = FDroidPopen(['wget', '-nv', url], cwd=tmp_dir)
if p.returncode != 0 or not os.path.exists(srcapk):
logging.error("...failed to retrieve " + url +
" - publish skipped")
continue
# Compare our unsigned one with the downloaded one...
compare_result = common.compare_apks(srcapk, apkfile, tmp_dir)
if compare_result:
logging.error("...verfication failed - publish skipped : "
+ compare_result)
continue
# Success! So move the downloaded file to the repo...
shutil.move(srcapk, os.path.join(output_dir, apkfilename))
else:
# It's a 'normal' app, i.e. we sign and publish it...
# Figure out the key alias name we'll use. Only the first 8
# characters are significant, so we'll use the first 8 from
# the MD5 of the app's ID and hope there are no collisions.

View File

@ -19,8 +19,6 @@
import sys
import os
import shutil
import subprocess
import glob
from optparse import OptionParser
import logging
@ -80,30 +78,16 @@ def main():
os.remove(remoteapk)
url = 'https://f-droid.org/repo/' + apkfilename
logging.info("...retrieving " + url)
p = FDroidPopen(['wget', url], cwd=tmp_dir)
p = FDroidPopen(['wget', '-nv', url], cwd=tmp_dir)
if p.returncode != 0:
raise FDroidException("Failed to get " + apkfilename)
thisdir = os.path.join(tmp_dir, 'this_apk')
thatdir = os.path.join(tmp_dir, 'that_apk')
for d in [thisdir, thatdir]:
if os.path.exists(d):
shutil.rmtree(d)
os.mkdir(d)
if subprocess.call(['jar', 'xf',
os.path.join("..", "..", unsigned_dir, apkfilename)],
cwd=thisdir) != 0:
raise FDroidException("Failed to unpack local build of " + apkfilename)
if subprocess.call(['jar', 'xf',
os.path.join("..", "..", remoteapk)],
cwd=thatdir) != 0:
raise FDroidException("Failed to unpack remote build of " + apkfilename)
p = FDroidPopen(['diff', '-r', 'this_apk', 'that_apk'], cwd=tmp_dir)
lines = p.output.splitlines()
if len(lines) != 1 or 'META-INF' not in lines[0]:
raise FDroidException("Unexpected diff output - " + p.output)
compare_result = common.compare_apks(
os.path.join(unsigned_dir, apkfilename),
remoteapk,
tmp_dir)
if compare_result:
raise FDroidException(compare_result)
logging.info("...successfully verified")
verified += 1