Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
*~
*.pyc
~*
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ Key concepts
===============================================
- Interact with the Cordova CLI directly from Python
- Enables building and archiving PhoneGap applications from your Python code
- Interact with Jarsigner and Zipalign to sign and verify android apks.


***Note:***
This uses Google's methodology of
[manually signing apks](http://developer.android.com/tools/publishing/app-signing.html#signing-manually)
using [Jarsigner] and [Zipalign] to verify and sign apks.


Usage
Expand All @@ -22,5 +29,31 @@ Usage
APPLICATION_ROOT
)

# Build a debug version for any platform application.
application.build('android') # or any installed platform
application.archive('ios') # or any installed platform
application.archive('ios') # or any installed platform

# Build a relase version for any platform application
application.build('android', release=True) # or any installed platform
application.build('ios', release=True) # or any installed platform

# Signing Android application (apk)
application.sign_android_apk(keystore="/path/to/keystore", keypass="passcode", storepass="passcode")



Requirements
============

* [NodeJs]
* [NPM]
* [Jarsigner]
* [Zipalign]
* [Cordova]


[NodeJs]: https://nodejs.org/en/
[NPM]: https://www.npmjs.com/
[Jarsigner]: http://docs.oracle.com/javase/6/docs/technotes/tools/windows/jarsigner.html
[Zipalign]: http://developer.android.com/tools/help/zipalign.html
[Cordova]: https://cordova.apache.org/
96 changes: 81 additions & 15 deletions cordova/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,18 @@
import os
import re
import subprocess
from datetime import datetime

from .decorators import for_all_methods, chdir_context


BUILD_LOCATION = {
'android': {
'debug': 'platforms/android/ant-build/%s-debug.apk',
'release': 'platforms/android/ant-build/%s-release-unsigned.apk',
'archive': 'platforms/%s-android.zip'
'debug': 'platforms/android/build/outputs/apk/%s-debug.apk',
'release': ('platforms/android/build/'
'outputs/apk/%s-release-unsigned.apk'),
'archive': 'platforms/%s-android.zip',
'signed': 'platforms/android/build/outputs/apk/%s-%s.apk'
},
'ios': {
'debug': 'platforms/ios/build/emulator/%s.app',
Expand All @@ -27,58 +30,66 @@
class App(object):
path = None
name = None
debug = False

# We need to initialize the application with the path of the root
# of the project
def __init__(self, path, name, *args, **kwargs):
def __init__(self, path, name, debug=False, *args, **kwargs):
self.path = path
self.name = name
self.debug = debug
super(App, self).__init__(*args, **kwargs)

def __platform_list(self):
"""List platforms supported by cordova."""
platform_ls_output = subprocess.check_output([
'cordova', 'platform', 'ls'
]).splitlines()
], shell=self.debug).splitlines()

installed = re.findall(r'[,:]\s(\w+)\s\d+', platform_ls_output[0])
available = re.findall(r'[,:]\s(\w+)\s', platform_ls_output[1])

return (installed, available)

def installed_platform_list(self):
"""List the installed platforms for the project."""
return self.__platform_list()[0]

def available_platform_list(self):
"""List the available platforms that can be used by the project."""
return self.__platform_list()[1]

def add_platform(self, platform):
"""Add supported platform to the project."""
return_code = subprocess.call([
'cordova', 'platform', 'add', platform
])
], shell=self.debug)

if return_code == 0:
return True
else:
return False

def remove_platform(self, platform):
"""Remove supported platfrom from the project."""
return_code = subprocess.call([
'cordova', 'platform', 'remove', platform
])
], shell=self.debug)

if return_code == 0:
return True
else:
return False

def archive(self, platform):
"""Archive the android project files into a zip file."""
os.chdir('platforms')
return_code = subprocess.call([
'tar', '-czf',
'%s-%s.zip' % (
self.name, platform
), platform
])
], shell=self.debug)

if return_code == 0:
return '%s/%s-%s.zip' % (
Expand All @@ -88,38 +99,93 @@ def archive(self, platform):
return False

def build(self, platform, release=False):
"""Build cordova app for the project."""
cmd_params = ['cordova', 'build', platform]

if release:
cmd_params.append('--release')

return_code = subprocess.call(cmd_params)
return_code = subprocess.call(cmd_params, shell=self.debug)

if return_code == 0:
return [os.path.join(
return os.path.join(
self.path,
BUILD_LOCATION[platform]
['release' if release else 'debug'] % (
self.name
))
]
'android' if platform == 'android' else self.name
)
)
else:
return False

def sign_android_apk(self, keystore, keypass, storepass,
unsigned_apk=None):
"""Sign android apk for the cordova project."""
if not unsigned_apk:
unsigned_apk = os.path.join(
self.path,
BUILD_LOCATION["android"]["release"] % 'android'
)

return_code = subprocess.call(
['jarsigner',
'-verbose',
'-sigalg',
'SHA1withRSA',
'-digestalg',
'SHA1',
'-keystore',
keystore,
'-tsa',
'http://tsa.starfieldtech.com',
'-storepass',
storepass,
'-keypass',
keypass,
unsigned_apk,
self.name
], shell=self.debug)

signed_apk_name = os.path.join(
self.path,
BUILD_LOCATION["android"]["signed"] % (
self.name,
datetime.today().strftime("%d-%m-%y")
)
)

if return_code == 0:
if os.path.exists(signed_apk_name):
os.remove(signed_apk_name)
return_code = subprocess.call(
[
"zipalign",
"-v",
"4",
unsigned_apk,
signed_apk_name
]
)
if return_code == 0:
return signed_apk_name
return False

def prepare(self, platform):
"""Prepare cordova app for the project."""
return_code = subprocess.call([
'cordova', 'prepare', platform
])
], shell=self.debug)

if return_code == 0:
return True
else:
return False

def compile(self, platform):
"""Compile the cordova source code without building the platform app."""
return_code = subprocess.call([
'cordova', 'compile', platform
])
], shell=self.debug)

if return_code == 0:
return True
Expand Down