chiark / gitweb /
new config option: per-app repos to support nightly build repos
authorHans-Christoph Steiner <hans@eds.org>
Thu, 20 Aug 2015 15:40:18 +0000 (17:40 +0200)
committerHans-Christoph Steiner <hans@eds.org>
Tue, 25 Aug 2015 14:55:52 +0000 (16:55 +0200)
For devs that want to build and distribute nightly builds of their apps
using the fdroid tools.  The core idea here is to make the fdroidserver
tool suite the single set of tools for all types of builds and releases.
That will hopefully drive more free software developers to make f-droid.org
an core channel for official releases.

examples/config.py
fdroidserver/common.py
fdroidserver/server.py
fdroidserver/update.py

index eed07d3c2251198fd3911deec6c1fe5bf6cf89a1..6026e5b4fc699a7a6748affd10b90f785106e3c7 100644 (file)
@@ -56,6 +56,12 @@ archive_description = """
 The repository of older versions of applications from the main demo repository.
 """
 
+# Normally, all apps are collected into a single app repository, like on
+# https://f-droid.org.  For certain situations, it is better to make a repo
+# that is made up of APKs only from a single app.  For example, an automated
+# build server that publishes nightly builds.
+# per_app_repos = True
+
 # `fdroid update` will create a link to the current version of a given app.
 # This provides a static path to the current APK.  To disable the creation of
 # this link, uncomment this:
index 3e085624a406a84829a8e117ed9e9513b49f246b..fe15910579b8da870a7484bc725ff8f560566e49 100644 (file)
@@ -58,6 +58,7 @@ default_config = {
     'mvn3': "mvn",
     'gradle': 'gradle',
     'sync_from_local_copy_dir': False,
+    'per_app_repos': False,
     'make_current_version_link': True,
     'current_version_name_source': 'Name',
     'update_stats': False,
@@ -2135,3 +2136,26 @@ def download_file(url, local_filename=None, dldir='tmp'):
                 f.write(chunk)
                 f.flush()
     return local_filename
+
+
+def get_per_app_repos():
+    '''per-app repos are dirs named with the packageName of a single app'''
+
+    # Android packageNames are Java packages, they may contain uppercase or
+    # lowercase letters ('A' through 'Z'), numbers, and underscores
+    # ('_'). However, individual package name parts may only start with
+    # letters. https://developer.android.com/guide/topics/manifest/manifest-element.html#package
+    p = re.compile('^([a-zA-Z][a-zA-Z0-9_]*(\\.[a-zA-Z][a-zA-Z0-9_]*)*)?$')
+
+    repos = []
+    for root, dirs, files in os.walk(os.getcwd()):
+        for d in dirs:
+            print 'checking', root, 'for', d
+            if d in ('archive', 'metadata', 'repo', 'srclibs', 'tmp'):
+                # standard parts of an fdroid repo, so never packageNames
+                continue
+            elif p.match(d) \
+                    and os.path.exists(os.path.join(d, 'fdroid', 'repo', 'index.jar')):
+                repos.append(d)
+        break
+    return repos
index 93447767ab91d0a6a259ad72c402a71281f3ee97..003dc39665d66468fa7e1d647bb8555c64ed0106 100644 (file)
@@ -285,6 +285,8 @@ def main():
         repo_sections.append('archive')
         if not os.path.exists('archive'):
             os.mkdir('archive')
+    if config['per_app_repos']:
+        repo_sections += common.get_per_app_repos()
 
     if args[0] == 'init':
         ssh = paramiko.SSHClient()
index 657259f976ef50cfac8234b2116dd560da70ce03..3c3a95fd2a165d07dc18c38550d95b68027ac77b 100644 (file)
@@ -1016,6 +1016,28 @@ def archive_old_apks(apps, apks, archapks, repodir, archivedir, defaultkeepversi
                 apks.remove(apk)
 
 
+def add_apks_to_per_app_repos(repodir, apks):
+    apks_per_app = dict()
+    for apk in apks:
+        apk['per_app_dir'] = os.path.join(apk['id'], 'fdroid')
+        apk['per_app_repo'] = os.path.join(apk['per_app_dir'], 'repo')
+        apk['per_app_icons'] = os.path.join(apk['per_app_repo'], 'icons')
+        apks_per_app[apk['id']] = apk
+
+        if not os.path.exists(apk['per_app_icons']):
+            logging.info('Adding new repo for only ' + apk['id'])
+            os.makedirs(apk['per_app_icons'])
+
+        apkpath = os.path.join(repodir, apk['apkname'])
+        shutil.copy(apkpath, apk['per_app_repo'])
+        apksigpath = apkpath + '.sig'
+        if os.path.exists(apksigpath):
+            shutil.copy(apksigpath, apk['per_app_repo'])
+        apkascpath = apkpath + '.asc'
+        if os.path.exists(apkascpath):
+            shutil.copy(apkascpath, apk['per_app_repo'])
+
+
 config = None
 options = None
 
@@ -1215,6 +1237,20 @@ def main():
     # name comes from there!)
     sortedids = sorted(apps.iterkeys(), key=lambda appid: apps[appid]['Name'].upper())
 
+    # APKs are placed into multiple repos based on the app package, providing
+    # per-app subscription feeds for nightly builds and things like it
+    if config['per_app_repos']:
+        add_apks_to_per_app_repos(repodirs[0], apks)
+        for appid, app in apps.iteritems():
+            repodir = os.path.join(appid, 'fdroid', 'repo')
+            appdict = dict()
+            appdict[appid] = app
+            if os.path.isdir(repodir):
+                make_index(appdict, [appid], apks, repodir, False, categories)
+            else:
+                logging.info('Skipping index generation for ' + appid)
+        return
+
     if len(repodirs) > 1:
         archive_old_apks(apps, apks, archapks, repodirs[0], repodirs[1], config['archive_older'])