chiark / gitweb /
Merge branch 'buildserver-auto-install' into 'master'
authorMichael Pöhn <uniq@h4x.at>
Fri, 17 Mar 2017 12:51:15 +0000 (12:51 +0000)
committerMichael Pöhn <uniq@h4x.at>
Fri, 17 Mar 2017 12:51:15 +0000 (12:51 +0000)
buildserver auto install

Closes #247

See merge request !229

examples/config.py
fdroidserver/build.py
fdroidserver/common.py
fdroidserver/server.py
fdroidserver/update.py
tests/metadata/fake.ota.update.txt
tests/run-tests

index eac1b995a7b057265983f63b2b174f1fd99acb89..20405b07c2f6fb13849fcf594ad9559865f3a44d 100644 (file)
@@ -238,6 +238,13 @@ The repository of older versions of applications from the main demo repository.
 # wiki_user = "login"
 # wiki_password = "1234"
 
+# Keep a log of all generated index files in a git repo to provide a
+# "binary transparency" log for anyone to check the history of the
+# binaries that are published.  This is in the form of a "git remote",
+# which this machine where `fdroid update` is run has already been
+# configured to allow push access (e.g. ssh key, username/password, etc)
+# binary_transparency_remote = "git@gitlab.com:fdroid/binary-transparency-log.git"
+
 # Only set this to true when running a repository where you want to generate
 # stats, and only then on the master build servers, not a development
 # machine.
index 00a20cf6cd5b81bc365609b0bcd28f78bd25f047..a90f532e74fd7cdcb93d026722b36ad12d61de83 100644 (file)
@@ -859,7 +859,8 @@ def build_local(app, build, vcs, build_dir, output_dir, srclib_dir, extlib_dir,
                        re.S | re.M).group(1)
         src = os.path.join(bindir, src)
     elif omethod == 'raw':
-        globpath = os.path.join(root_dir, build.output)
+        output_path = common.replace_build_vars(build.output, build)
+        globpath = os.path.join(root_dir, output_path)
         apks = glob.glob(globpath)
         if len(apks) > 1:
             raise BuildException('Multiple apks match %s' % globpath, '\n'.join(apks))
index b8a506a55c5495ca2d7dacb11ce8d46d80adb0b7..673d57af9de7dce4517c5f121c495a5209134dbb 100644 (file)
@@ -1917,15 +1917,20 @@ def set_FDroidPopen_env(build=None):
             env[n] = build.ndk_path()
 
 
+def replace_build_vars(cmd, build):
+    cmd = cmd.replace('$$COMMIT$$', build.commit)
+    cmd = cmd.replace('$$VERSION$$', build.versionName)
+    cmd = cmd.replace('$$VERCODE$$', build.versionCode)
+    return cmd
+
+
 def replace_config_vars(cmd, build):
     cmd = cmd.replace('$$SDK$$', config['sdk_path'])
     cmd = cmd.replace('$$NDK$$', build.ndk_path())
     cmd = cmd.replace('$$MVN3$$', config['mvn3'])
     cmd = cmd.replace('$$QT$$', config['qt_sdk_path'] or '')
     if build is not None:
-        cmd = cmd.replace('$$COMMIT$$', build.commit)
-        cmd = cmd.replace('$$VERSION$$', build.versionName)
-        cmd = cmd.replace('$$VERCODE$$', build.versionCode)
+        cmd = replace_build_vars(cmd, build)
     return cmd
 
 
@@ -1998,6 +2003,23 @@ def compare_apks(apk1, apk2, tmp_dir):
     trying to do the comparison.
     """
 
+    absapk1 = os.path.abspath(apk1)
+    absapk2 = os.path.abspath(apk2)
+
+    # try to find diffoscope in the path, if it hasn't been manually configed
+    if 'diffoscope' not in config:
+        tmp = find_command('diffoscope')
+        if tmp is not None:
+            config['diffoscope'] = tmp
+    if 'diffoscope' in config:
+        htmlfile = absapk1 + '.diffoscope.html'
+        textfile = absapk1 + '.diffoscope.txt'
+        if subprocess.call([config['diffoscope'],
+                            '--max-report-size', '12345678', '--max-diff-block-lines', '100',
+                            '--html', htmlfile, '--text', textfile,
+                            absapk1, absapk2]) != 0:
+            return("Failed to unpack " + apk1)
+
     apk1dir = os.path.join(tmp_dir, apk_badchars.sub('_', apk1[0:-4]))  # trim .apk
     apk2dir = os.path.join(tmp_dir, apk_badchars.sub('_', apk2[0:-4]))  # trim .apk
     for d in [apk1dir, apk2dir]:
index 2213d788ae76e809471ce36e4b1ee423550e81db..998b80cf3f73ae835fb3a3657c65be69ad98cf43 100644 (file)
@@ -279,6 +279,22 @@ def upload_to_virustotal(repo_section, vt_apikey):
             logging.info(response['verbose_msg'] + " " + response['permalink'])
 
 
+def push_binary_transparency(binary_transparency_remote):
+    '''push the binary transparency git repo to the specifed remote'''
+    import git
+
+    repo = git.Repo('binary_transparency_log')
+    pushremote = None
+    for remote in repo.remotes:
+        if remote.url == binary_transparency_remote:
+            pushremote = remote
+            break
+
+    if not pushremote:
+        pushremote = repo.create_remote('fdroid_server_update', binary_transparency_remote)
+    pushremote.push('master')
+
+
 def main():
     global config, options
 
@@ -414,6 +430,11 @@ def main():
                 upload_to_android_observatory(repo_section)
             if config.get('virustotal_apikey'):
                 upload_to_virustotal(repo_section, config.get('virustotal_apikey'))
+
+            binary_transparency_remote = config.get('binary_transparency_remote')
+            if binary_transparency_remote:
+                push_binary_transparency(binary_transparency_remote)
+
     sys.exit(0)
 
 
index 5189633cc70bafbfb13438f11ce2cfd364870b32..3d5e3ad6ddf1a504609f668d35191a5337d9a567 100644 (file)
@@ -23,11 +23,13 @@ import sys
 import os
 import shutil
 import glob
+import json
 import re
 import socket
 import zipfile
 import hashlib
 import pickle
+import platform
 import urllib.parse
 from datetime import datetime, timedelta
 from xml.dom.minidom import Document
@@ -1439,6 +1441,80 @@ def add_apks_to_per_app_repos(repodir, apks):
             shutil.copy(apkascpath, apk['per_app_repo'])
 
 
+def make_binary_transparency_log(repodirs):
+    '''Log the indexes in a standalone git repo to serve as a "binary
+    transparency" log.
+
+    see: https://www.eff.org/deeplinks/2014/02/open-letter-to-tech-companies
+
+    '''
+
+    import git
+    btrepo = 'binary_transparency'
+    if os.path.exists(os.path.join(btrepo, '.git')):
+        gitrepo = git.Repo(btrepo)
+    else:
+        if not os.path.exists(btrepo):
+            os.mkdir(btrepo)
+        gitrepo = git.Repo.init(btrepo)
+
+        gitconfig = gitrepo.config_writer()
+        gitconfig.set_value('user', 'name', 'fdroid update')
+        gitconfig.set_value('user', 'email', 'fdroid@' + platform.node())
+
+        url = config['repo_url'].rstrip('/')
+        with open(os.path.join(btrepo, 'README.md'), 'w') as fp:
+            fp.write("""
+# Binary Transparency Log for %s
+
+""" % url[:url.rindex('/')])  # strip '/repo'
+        gitrepo.index.add(['README.md', ])
+        gitrepo.index.commit('add README')
+
+    for repodir in repodirs:
+        cpdir = os.path.join(btrepo, repodir)
+        if not os.path.exists(cpdir):
+            os.mkdir(cpdir)
+        for f in ('index.xml', ):
+            dest = os.path.join(cpdir, f)
+            shutil.copyfile(os.path.join(repodir, f), dest)
+            gitrepo.index.add([os.path.join(repodir, f), ])
+        for f in ('index.jar', ):
+            repof = os.path.join(repodir, f)
+            dest = os.path.join(cpdir, f)
+            jarin = zipfile.ZipFile(repof, 'r')
+            jarout = zipfile.ZipFile(dest, 'w')
+            for info in jarin.infolist():
+                if info.filename.startswith('META-INF/'):
+                    jarout.writestr(info, jarin.read(info.filename))
+            jarout.close()
+            jarin.close()
+            gitrepo.index.add([repof, ])
+
+        files = []
+        for root, dirs, filenames in os.walk(repodir):
+            for f in filenames:
+                files.append(os.path.relpath(os.path.join(root, f), repodir))
+        output = collections.OrderedDict()
+        for f in sorted(files):
+            repofile = os.path.join(repodir, f)
+            stat = os.stat(repofile)
+            output[f] = (
+                stat.st_size,
+                stat.st_ctime_ns,
+                stat.st_mtime_ns,
+                stat.st_mode,
+                stat.st_uid,
+                stat.st_gid,
+            )
+        fslogfile = os.path.join(cpdir, 'filesystemlog.json')
+        with open(fslogfile, 'w') as fp:
+            json.dump(output, fp, indent=2)
+        gitrepo.index.add([os.path.join(repodir, 'filesystemlog.json'), ])
+
+    gitrepo.index.commit('fdroid update')
+
+
 config = None
 options = None
 
@@ -1678,6 +1754,9 @@ def main():
     if len(repodirs) > 1:
         make_index(apps, sortedids, archapks, repodirs[1], True)
 
+    if config.get('binary_transparency_remote'):
+        make_binary_transparency_log(repodirs)
+
     if config['update_stats']:
 
         # Update known apks info...
index 264ba83327b3e72af2b3713098ecf2271e52e080..165da01484f575b94404da0a1293542a5d2e4085 100644 (file)
@@ -33,7 +33,7 @@ Repo:https://gitlab.com/fdroid/privileged-extension.git
 
 Build:0.2.1,2000
     commit=0.2.1
-    output=app/build/distributions/FDroidPrivilegedExtensionFromBinaries-0.2.1.zip
+    output=app/build/distributions/FDroidPrivilegedExtensionFromBinaries-$$VERSION$$.zip
     build=gradle assembleUpdateZipFromBinariesDebug
 
 Auto Update Mode:Version %v
index 027d1cec85d2b4bcb5fda697ca2f2414de4a31a7..66c4b2a159e445a5018aafc8952e647ed3153c26 100755 (executable)
@@ -553,6 +553,27 @@ fi
 set -e
 
 
+#------------------------------------------------------------------------------#
+echo_header "copy tests/repo, update with binary transparency log"
+
+REPOROOT=`create_test_dir`
+GNUPGHOME=$REPOROOT/gnupghome
+KEYSTORE=$WORKSPACE/tests/keystore.jks
+cd $REPOROOT
+$fdroid init --keystore $KEYSTORE --repo-keyalias=sova
+cp -a $WORKSPACE/tests/metadata $WORKSPACE/tests/repo $WORKSPACE/tests/stats $REPOROOT/
+echo 'keystorepass = "r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI="' >> config.py
+echo 'keypass = "r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI="' >> config.py
+echo 'binary_transparency_remote = "git@gitlab.com:fdroid-continuous-integration/binary-transparency.git"' >> config.py
+echo "accepted_formats = ['json', 'txt', 'yml']" >> config.py
+$fdroid update --verbose --pretty
+test -e repo/index.xml
+test -e repo/index.jar
+grep -F '<application id=' repo/index.xml > /dev/null
+cd binary_transparency
+[ `git rev-list --count HEAD` == "2" ]
+
+
 #------------------------------------------------------------------------------#
 echo_header "setup a new repo with keystore with APK, update, then without key"