3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 Sign Android packages using an Android debug keystore, creating the
9 keystore if it does not exist.
11 This and |zip| can be combined to replace the Android |apkbuilder|
12 tool, which was deprecated in SDK r22.
14 Exits with code 0 if creating the keystore and every signing succeeded,
15 or with code 1 if any creation or signing failed.
18 from argparse
import ArgumentParser
26 log
= logging
.getLogger(os
.path
.basename(__file__
))
27 log
.setLevel(logging
.INFO
)
28 sh
= logging
.StreamHandler(stream
=sys
.stdout
)
29 sh
.setFormatter(logging
.Formatter('%(name)s: %(message)s'))
35 A thin abstraction on top of an Android debug key store.
37 def __init__(self
, keystore
):
38 self
._keystore
= os
.path
.abspath(os
.path
.expanduser(keystore
))
39 self
._alias
= 'androiddebugkey'
41 self
.keytool
= 'keytool'
42 self
.jarsigner
= 'jarsigner'
52 def _check(self
, args
):
55 subprocess
.check_call(args
)
57 subprocess
.check_output(args
)
59 if ex
.errno
!= errno
.ENOENT
:
61 raise Exception("Could not find executable '%s'" % args
[0])
63 def keystore_contains_alias(self
):
64 args
= [ self
.keytool
,
66 '-keystore', self
.keystore
,
67 '-storepass', 'android',
75 except subprocess
.CalledProcessError
as e
:
78 log
.info('Keystore %s %s alias %s' %
80 'contains' if contains
else 'does not contain',
84 def create_alias_in_keystore(self
):
86 path
= os
.path
.dirname(self
.keystore
)
88 except OSError as exception
:
89 if exception
.errno
!= errno
.EEXIST
:
92 args
= [ self
.keytool
,
94 '-keystore', self
.keystore
,
95 '-storepass', 'android',
97 '-keypass', 'android',
98 '-dname', 'CN=Android Debug,O=Android,C=US',
106 log
.info('Created alias %s in keystore %s' %
107 (self
.alias
, self
.keystore
))
110 if not self
.keystore_contains_alias():
111 self
.create_alias_in_keystore()
113 args
= [ self
.jarsigner
,
114 '-digestalg', 'SHA1',
115 '-sigalg', 'MD5withRSA',
116 '-keystore', self
.keystore
,
117 '-storepass', 'android',
122 args
.append('-verbose')
125 log
.info('Signed %s with alias %s from keystore %s' %
126 (apk
, self
.alias
, self
.keystore
))
129 def parse_args(argv
):
130 parser
= ArgumentParser(description
='Sign Android packages using an Android debug keystore.')
131 parser
.add_argument('apks', nargs
='+',
133 help='Android packages to be signed')
134 parser
.add_argument('-v', '--verbose',
138 help='verbose output')
139 parser
.add_argument('--keytool',
142 help='path to Java keytool')
143 parser
.add_argument('--jarsigner',
146 help='path to Java jarsigner')
147 parser
.add_argument('--keystore',
149 default
='~/.android/debug.keystore',
150 help='path to keystore (default: ~/.android/debug.keystore)')
151 parser
.add_argument('-f', '--force-create-keystore',
155 help='force creating keystore')
156 return parser
.parse_args(argv
)
160 args
= parse_args(sys
.argv
[1:])
162 keystore
= DebugKeystore(args
.keystore
)
163 keystore
.verbose
= args
.verbose
164 keystore
.keytool
= args
.keytool
165 keystore
.jarsigner
= args
.jarsigner
169 keystore
.create_alias_in_keystore()
170 except subprocess
.CalledProcessError
as e
:
171 log
.error('Failed to force-create alias %s in keystore %s' %
172 (keystore
.alias
, keystore
.keystore
))
176 for apk
in args
.apks
:
179 except subprocess
.CalledProcessError
as e
:
180 log
.error('Failed to sign %s', apk
)
186 if __name__
== '__main__':