chiark / gitweb /
Separated build (compile) from publish (sign)
authorCiaran Gultnieks <ciaran@ciarang.com>
Thu, 2 Feb 2012 14:27:49 +0000 (14:27 +0000)
committerCiaran Gultnieks <ciaran@ciarang.com>
Thu, 2 Feb 2012 14:27:49 +0000 (14:27 +0000)
build.py
publish.py [new file with mode: 0755]

index b2c4c852a96fda3732c1670fa30df361501c6e5c..4e07ceb9fa88152bc1783715946d975e5b3aa8c9 100755 (executable)
--- a/build.py
+++ b/build.py
@@ -24,8 +24,6 @@ import subprocess
 import re
 import zipfile
 import tarfile
-import md5
-import shlex
 from xml.dom.minidom import Document
 from optparse import OptionParser
 
@@ -67,11 +65,13 @@ if not os.path.isdir(tmp_dir):
 if options.test:
     output_dir = tmp_dir
 else:
-    output_dir = 'repo'
+    output_dir = 'unsigned'
     if not os.path.isdir(output_dir):
         print "Creating output directory"
         os.makedirs(output_dir)
 
+repo_dir = 'repo'
+
 build_dir = 'build'
 if not os.path.isdir(build_dir):
     print "Creating build directory"
@@ -104,10 +104,10 @@ for app in apps:
             try:
                 dest = os.path.join(output_dir, app['id'] + '_' +
                         thisbuild['vercode'] + '.apk')
-                dest_unsigned = os.path.join(tmp_dir, app['id'] + '_' +
-                        thisbuild['vercode'] + '_unsigned.apk')
+                dest_repo = os.path.join(repo_dir, app['id'] + '_' +
+                        thisbuild['vercode'] + '.apk')
 
-                if os.path.exists(dest):
+                if os.path.exists(dest) or os.path.exists(dest_repo):
                     if options.verbose:
                         print "..version " + thisbuild['version'] + " already exists"
                 elif thisbuild['commit'].startswith('!'):
@@ -228,67 +228,15 @@ for app in apps:
                                              % (version, str(vercode), thisbuild['version'], str(thisbuild['vercode']))
                                             )
 
-                    # Copy the unsigned apk to our temp directory for further
-                    # processing...
-                    shutil.copyfile(src, dest_unsigned)
-
-                    # 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.
-                    # If a collision does occur later, we're going to have to
-                    # come up with a new alogrithm, AND rename all existing keys
-                    # in the keystore!
-                    if keyaliases.has_key(app['id']):
-                        # For this particular app, the key alias is overridden...
-                        keyalias = keyaliases[app['id']]
-                    else:
-                        m = md5.new()
-                        m.update(app['id'])
-                        keyalias = m.hexdigest()[:8]
-                    print "Key alias: " + keyalias
-
-                    # See if we already have a key for this application, and
-                    # if not generate one...
-                    p = subprocess.Popen(['keytool', '-list',
-                        '-alias', keyalias, '-keystore', keystore,
-                        '-storepass', keystorepass], stdout=subprocess.PIPE)
-                    output = p.communicate()[0]
-                    if p.returncode !=0:
-                        print "Key does not exist - generating..."
-                        p = subprocess.Popen(['keytool', '-genkey',
-                            '-keystore', keystore, '-alias', keyalias,
-                            '-keyalg', 'RSA', '-keysize', '2048',
-                            '-validity', '10000',
-                            '-storepass', keystorepass, '-keypass', keypass,
-                            '-dname', keydname], stdout=subprocess.PIPE)
-                        output = p.communicate()[0]
-                        print output
-                        if p.returncode != 0:
-                            raise BuildException("Failed to generate key")
-
-                    # Sign the application...
-                    p = subprocess.Popen(['jarsigner', '-keystore', keystore,
-                        '-storepass', keystorepass, '-keypass', keypass,
-                            dest_unsigned, keyalias], stdout=subprocess.PIPE)
-                    output = p.communicate()[0]
-                    print output
-                    if p.returncode != 0:
-                        raise BuildException("Failed to sign application")
-
-                    # Zipalign it...
-                    p = subprocess.Popen([os.path.join(sdk_path,'tools','zipalign'),
-                                        '-v', '4', dest_unsigned, dest],
-                                        stdout=subprocess.PIPE)
-                    output = p.communicate()[0]
-                    print output
-                    if p.returncode != 0:
-                        raise BuildException("Failed to align application")
-                    os.remove(dest_unsigned)
+                    # Copy the unsigned apk to our destination directory for further
+                    # processing (by publish.py)...
+                    shutil.copyfile(src, dest)
 
                     # Move the source tarball into the output directory...
                     if output_dir != tmp_dir:
-                        shutil.move(os.path.join(tmp_dir, tarname + '.tar.gz'),
-                            os.path.join(output_dir, tarname + '.tar.gz'))
+                        tarfilename = tarname + '.tar.gz'
+                        shutil.move(os.path.join(tmp_dir, tarfilename),
+                            os.path.join(output_dir, tarfilename))
 
                     build_succeeded.append(app)
             except BuildException as be:
diff --git a/publish.py b/publish.py
new file mode 100755 (executable)
index 0000000..7fd9a1f
--- /dev/null
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# publish.py - part of the FDroid server tools
+# Copyright (C) 2010-12, Ciaran Gultnieks, ciaran@ciarang.com
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import sys
+import os
+import shutil
+import subprocess
+import re
+import zipfile
+import tarfile
+import md5
+import glob
+from optparse import OptionParser
+
+import common
+from common import BuildException
+
+#Read configuration...
+execfile('config.py')
+
+# Parse command line...
+parser = OptionParser()
+parser.add_option("-v", "--verbose", action="store_true", default=False,
+                  help="Spew out even more information than normal")
+parser.add_option("-p", "--package", default=None,
+                  help="Publish only the specified package")
+(options, args) = parser.parse_args()
+
+log_dir = 'logs'
+if not os.path.isdir(log_dir):
+    print "Creating log directory"
+    os.makedirs(log_dir)
+
+tmp_dir = 'tmp'
+if not os.path.isdir(tmp_dir):
+    print "Creating temporary directory"
+    os.makedirs(tmp_dir)
+
+output_dir = 'repo'
+if not os.path.isdir(output_dir):
+    print "Creating output directory"
+    os.makedirs(output_dir)
+
+unsigned_dir = 'unsigned'
+if not os.path.isdir(unsigned_dir):
+    print "No unsigned directory - nothing to do"
+    sys.exit(0)
+
+for apkfile in sorted(glob.glob(os.path.join(unsigned_dir, '*.apk'))):
+
+    apkfilename = os.path.basename(apkfile)
+    i = apkfilename.rfind('_')
+    if i == -1:
+        raise BuildException("Invalid apk name")
+    appid = apkfilename[:i]
+    print "Processing " + appid
+
+    if not options.package or options.package == appid:
+
+        # 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.
+        # If a collision does occur later, we're going to have to
+        # come up with a new alogrithm, AND rename all existing keys
+        # in the keystore!
+        if keyaliases.has_key(appid):
+            # For this particular app, the key alias is overridden...
+            keyalias = keyaliases[appid]
+        else:
+            m = md5.new()
+            m.update(appid)
+            keyalias = m.hexdigest()[:8]
+        print "Key alias: " + keyalias
+
+        # See if we already have a key for this application, and
+        # if not generate one...
+        p = subprocess.Popen(['keytool', '-list',
+            '-alias', keyalias, '-keystore', keystore,
+            '-storepass', keystorepass], stdout=subprocess.PIPE)
+        output = p.communicate()[0]
+        if p.returncode !=0:
+            print "Key does not exist - generating..."
+            p = subprocess.Popen(['keytool', '-genkey',
+                '-keystore', keystore, '-alias', keyalias,
+                '-keyalg', 'RSA', '-keysize', '2048',
+                '-validity', '10000',
+                '-storepass', keystorepass, '-keypass', keypass,
+                '-dname', keydname], stdout=subprocess.PIPE)
+            output = p.communicate()[0]
+            print output
+            if p.returncode != 0:
+                raise BuildException("Failed to generate key")
+
+        # Sign the application...
+        p = subprocess.Popen(['jarsigner', '-keystore', keystore,
+            '-storepass', keystorepass, '-keypass', keypass,
+                apkfile, keyalias], stdout=subprocess.PIPE)
+        output = p.communicate()[0]
+        print output
+        if p.returncode != 0:
+            raise BuildException("Failed to sign application")
+
+        # Zipalign it...
+        p = subprocess.Popen([os.path.join(sdk_path,'tools','zipalign'),
+                            '-v', '4', apkfile,
+                            os.path.join(output_dir, apkfilename)],
+                            stdout=subprocess.PIPE)
+        output = p.communicate()[0]
+        print output
+        if p.returncode != 0:
+            raise BuildException("Failed to align application")
+        os.remove(apkfile)
+
+        # Move the source tarball into the output directory...
+        tarfilename = apkfilename[:-4] + '_src.tar.gz'
+        shutil.move(os.path.join(unsigned_dir, tarfilename),
+                os.path.join(output_dir, tarfilename))
+
+        print 'Published ' + apkfilename
+