chiark / gitweb /
Merge commit 'refs/merge-requests/122' of git://gitorious.org/f-droid/fdroidserver...
[fdroidserver.git] / fdroidserver / 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 appid in keyaliases:
85                 # For this particular app, the key alias is overridden...
86                 keyalias = keyaliases[appid]
87                 if keyalias.startswith('@'):
88                     m = md5.new()
89                     m.update(keyalias[1:])
90                     keyalias = m.hexdigest()[:8]
91             else:
92                 m = md5.new()
93                 m.update(appid)
94                 keyalias = m.hexdigest()[:8]
95             print "Key alias: " + keyalias
96
97             # See if we already have a key for this application, and
98             # if not generate one...
99             p = subprocess.Popen(['keytool', '-list',
100                 '-alias', keyalias, '-keystore', keystore,
101                 '-storepass', keystorepass], stdout=subprocess.PIPE)
102             output = p.communicate()[0]
103             if p.returncode !=0:
104                 print "Key does not exist - generating..."
105                 p = subprocess.Popen(['keytool', '-genkey',
106                     '-keystore', keystore, '-alias', keyalias,
107                     '-keyalg', 'RSA', '-keysize', '2048',
108                     '-validity', '10000',
109                     '-storepass', keystorepass, '-keypass', keypass,
110                     '-dname', keydname], stdout=subprocess.PIPE)
111                 output = p.communicate()[0]
112                 print output
113                 if p.returncode != 0:
114                     raise BuildException("Failed to generate key")
115
116             # Sign the application...
117             p = subprocess.Popen(['jarsigner', '-keystore', keystore,
118                 '-storepass', keystorepass, '-keypass', keypass, '-sigalg',
119                 'MD5withRSA', '-digestalg', 'SHA1',
120                     apkfile, keyalias], stdout=subprocess.PIPE)
121             output = p.communicate()[0]
122             print output
123             if p.returncode != 0:
124                 raise BuildException("Failed to sign application")
125
126             # Zipalign it...
127             p = subprocess.Popen([os.path.join(sdk_path,'tools','zipalign'),
128                                 '-v', '4', apkfile,
129                                 os.path.join(output_dir, apkfilename)],
130                                 stdout=subprocess.PIPE)
131             output = p.communicate()[0]
132             print output
133             if p.returncode != 0:
134                 raise BuildException("Failed to align application")
135             os.remove(apkfile)
136
137             # Move the source tarball into the output directory...
138             tarfilename = apkfilename[:-4] + '_src.tar.gz'
139             shutil.move(os.path.join(unsigned_dir, tarfilename),
140                     os.path.join(output_dir, tarfilename))
141
142             print 'Published ' + apkfilename
143
144
145 if __name__ == "__main__":
146     main()
147