chiark / gitweb /
be92c3ed17952ab5293f3cdaec82e1e5ee65b351
[fdroidserver.git] / publish.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # publish.py - part of the FDroid server tools
5 # Copyright (C) 2010-12, Ciaran Gultnieks, ciaran@ciarang.com
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU Affero General Public License for more details.
16 #
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20 import sys
21 import os
22 import shutil
23 import subprocess
24 import re
25 import zipfile
26 import tarfile
27 import md5
28 import glob
29 from optparse import OptionParser
30
31 import common
32 from common import BuildException
33
34 def main():
35
36     #Read configuration...
37     execfile('config.py', globals())
38
39     # Parse command line...
40     parser = OptionParser()
41     parser.add_option("-v", "--verbose", action="store_true", default=False,
42                       help="Spew out even more information than normal")
43     parser.add_option("-p", "--package", default=None,
44                       help="Publish only the specified package")
45     (options, args) = parser.parse_args()
46
47     log_dir = 'logs'
48     if not os.path.isdir(log_dir):
49         print "Creating log directory"
50         os.makedirs(log_dir)
51
52     tmp_dir = 'tmp'
53     if not os.path.isdir(tmp_dir):
54         print "Creating temporary directory"
55         os.makedirs(tmp_dir)
56
57     output_dir = 'repo'
58     if not os.path.isdir(output_dir):
59         print "Creating output directory"
60         os.makedirs(output_dir)
61
62     unsigned_dir = 'unsigned'
63     if not os.path.isdir(unsigned_dir):
64         print "No unsigned directory - nothing to do"
65         sys.exit(0)
66
67     for apkfile in sorted(glob.glob(os.path.join(unsigned_dir, '*.apk'))):
68
69         apkfilename = os.path.basename(apkfile)
70         i = apkfilename.rfind('_')
71         if i == -1:
72             raise BuildException("Invalid apk name")
73         appid = apkfilename[:i]
74         print "Processing " + appid
75
76         if not options.package or options.package == appid:
77
78             # Figure out the key alias name we'll use. Only the first 8
79             # characters are significant, so we'll use the first 8 from
80             # the MD5 of the app's ID and hope there are no collisions.
81             # If a collision does occur later, we're going to have to
82             # come up with a new alogrithm, AND rename all existing keys
83             # in the keystore!
84             if keyaliases.has_key(appid):
85                 # For this particular app, the key alias is overridden...
86                 keyalias = keyaliases[appid]
87             else:
88                 m = md5.new()
89                 m.update(appid)
90                 keyalias = m.hexdigest()[:8]
91             print "Key alias: " + keyalias
92
93             # See if we already have a key for this application, and
94             # if not generate one...
95             p = subprocess.Popen(['keytool', '-list',
96                 '-alias', keyalias, '-keystore', keystore,
97                 '-storepass', keystorepass], stdout=subprocess.PIPE)
98             output = p.communicate()[0]
99             if p.returncode !=0:
100                 print "Key does not exist - generating..."
101                 p = subprocess.Popen(['keytool', '-genkey',
102                     '-keystore', keystore, '-alias', keyalias,
103                     '-keyalg', 'RSA', '-keysize', '2048',
104                     '-validity', '10000',
105                     '-storepass', keystorepass, '-keypass', keypass,
106                     '-dname', keydname], stdout=subprocess.PIPE)
107                 output = p.communicate()[0]
108                 print output
109                 if p.returncode != 0:
110                     raise BuildException("Failed to generate key")
111
112             # Sign the application...
113             p = subprocess.Popen(['jarsigner', '-keystore', keystore,
114                 '-storepass', keystorepass, '-keypass', keypass,
115                     apkfile, keyalias], stdout=subprocess.PIPE)
116             output = p.communicate()[0]
117             print output
118             if p.returncode != 0:
119                 raise BuildException("Failed to sign application")
120
121             # Zipalign it...
122             p = subprocess.Popen([os.path.join(sdk_path,'tools','zipalign'),
123                                 '-v', '4', apkfile,
124                                 os.path.join(output_dir, apkfilename)],
125                                 stdout=subprocess.PIPE)
126             output = p.communicate()[0]
127             print output
128             if p.returncode != 0:
129                 raise BuildException("Failed to align application")
130             os.remove(apkfile)
131
132             # Move the source tarball into the output directory...
133             tarfilename = apkfilename[:-4] + '_src.tar.gz'
134             shutil.move(os.path.join(unsigned_dir, tarfilename),
135                     os.path.join(output_dir, tarfilename))
136
137             print 'Published ' + apkfilename
138
139
140 if __name__ == "__main__":
141     main()
142