Automated packaging, building, and deployment script for iOS/Android Sencha Touch + Phonegap apps

If you use Sencha Touch + Phonegap to develop apps for multiple platforms, you’ve likely run into the issue of how to manage OS-specific files and build components. For example, I use very different SASS stylesheets for iOS compared to Android, and certainly don’t wish to include unnecessary elements in the final Phonegap packaging for either platform. Of course there’s also the matter of separate OS-specific cordova.js files. Additionally, the steps of packaging the app with Sencha Cmd, then copying the output twice to both my Android and iOS native app’s www/ folder proved quite tedious. Lastly, If you’re using the “freemium” business model and maintain both free and paid versions of your app, the previous step is doubled. As such, I created a quick-and-dirty Python script to automate the build task in it’s entirety, which I’ve copied below.

I keep a common app.json & a _base.scss with shared elements in my Sencha Touch app directory. During each build executed by the script, the appropriate files for each OS are combined with the base files and output to the final native project directory (in a separate project location). After completion, a .SUCCESS or .ERROR file is created to indicate build success or any errors encountered in the event of failure.

If you use this script, be sure to change the directories to reflect that of your own project setups. Save the script as whatever_you_want.py in your Sencha app directory, and simply execute the script. The script is currently configured for iOS and Android, but can easily be extended to incorporate any Sencha/Phonegap supported OS.

custom_js_packager.py

import os, shutil, datetime
from subprocess import call

baseDir =  r"C:\Users\Brandon\Development\git"
senchaDir = baseDir + r"\caffeine\src\main\webapp\mobile"
senchaPackageDir = senchaDir + r"\build\mobile\package"
senchaSASSDir = senchaDir + r"\resources\sass" 

androidAppJSON = senchaDir + r"\app-android.json"
iosAppJSON = senchaDir + r"\app-ios.json"
appJSON = senchaDir + r"\app.json"

androidSCSS = senchaSASSDir + r"\_android.scss"
iosSCSS = senchaSASSDir + r"\_ios.scss"
appSCSS = senchaSASSDir + r"\app.scss"

androidProjectBaseDir = baseDir + r"\caffeine-android"
androidCaffeineDir = androidProjectBaseDir + r"\com.hawkinbj.CaffeinePro.Caffeine"
androidCaffeineProDir = androidProjectBaseDir + r"\com.hawkinbj.Caffeine.Caffeine"
androidWWWDir = r"\assets\www"

iosProjectBaseDir = baseDir + r"\caffeine-ios"
iosCaffeineDir = iosProjectBaseDir + r"\caffeine-ios"
iosCaffeineProDir = iosProjectBaseDir + r"\caffeine-ios-pro"
iosWWWDir = r"\www"

SUCCESS_EXT = ".SUCCESS"
ERROR_FILENAME = "error.ERROR"

def run():
    do_android()
    do_ios()

    print "done!"

def base_do(jsonFileName, scssFileName):
    remove_previous_info_files(jsonFileName)
    clean_package_dir()
    prepare_json(jsonFileName)
    prepare_scss(scssFileName)
    sencha_package()
    create_success_file(jsonFileName)

def do_android():
    print "doing android"
    base_do(androidAppJSON, androidSCSS)
    update_android_www()
    print "finished android"

def do_ios():
    print "doing ios"
    base_do(iosAppJSON, iosSCSS)
    update_ios_www()
    print "finished ios"

def remove_previous_info_files(osName):
	delete_success_file(osName)
	delete_error_file()

def delete_success_file(osName):
    try:
        os.remove(osName + SUCCESS_EXT)
    except:
        pass

def delete_error_file():
    try:
        os.remove(ERROR_FILENAME)
    except:
        pass

def create_success_file(fileName):
    create_and_write_file(fileName + SUCCESS_EXT, "success")

def create_error_file(errorMsg):
    create_and_write_file(ERROR_FILENAME, errorMsg)

def create_and_write_file(fileName, text):
    file = open(os.path.join(senchaDir, fileName), 'w+')
    if text:	
        file.write(str(datetime.datetime.now()) + " " + text)
    file.close()

def prepare_json(fileName):
    errorMsg = "OS-specific app.json files are missing!"

    if os.path.exists(fileName):
        copy_file(fileName, appJSON)
    else:
        create_error_file(errorMsg)
        raise Exception(errorMsg)

def prepare_scss(fileName):
    errorMsg = "OS-specific app.scss files are missing!"

    if os.path.exists(fileName):
        copy_file(fileName, appSCSS)
    else:
        create_error_file(errorMsg)
        raise Exception(errorMsg)

def copy_file(src, dest):
    shutil.copyfile(src, dest)

def copy_tree(src, dest):
    try:
        shutil.copytree(src, dest, symlinks=False)
    except:
        pass

def remove_tree(path):
    shutil.rmtree(path, ignore_errors=True)

def sencha_package():
    call("sencha app build package", cwd=senchaDir)

def update_android_www():
    proAssets = androidCaffeineProDir + androidWWWDir
    regAssets = androidCaffeineDir + androidWWWDir

    base_update_www(proAssets)
    base_update_www(regAssets)

def base_update_www(path):
    remove_tree(path)
    copy_tree(senchaPackageDir, path) 

def update_ios_www():
    proAssets = iosCaffeineProDir + iosWWWDir
    regAssets = iosCaffeineDir + iosWWWDir

    base_update_www(proAssets)
    base_update_www(regAssets)

def clean_package_dir():
    remove_tree(senchaPackageDir)

run()

if you have any questions, leave a comment and I’ll get back to you. Hopefully others find this useful or are inspired to roll their own solution. Cheers!

Requirements are Sencha Cmd (environment variable must be set) and Python.

Google’s Android Devices Emerge as Viable Challengers to Apple’s iPhone

Android Logo
Since it’s unveiling 3 years ago, Apple’s iPhone has set the bar for aspiring rival smartphone developers with carrier-specific endorsements (AT&T), advertising strategies (“There’s an app for that”), and direct consumer purchasing point-of-sale techniques (the Apple Store and App Store). Until this point, no individual or combinatorial effort by any carrier, developer, or hardware manufacturer has even come close to challenging the iPhone as the reigning consumer-wide smartphone king. Until now that is: enter Google and their Android mobile operating system.

Everyone knows by now either someone who owns an Android-powered mobile phone or, at the very least, is aware of their existence and subsequent emergence onto the mobile scene. From rampant television/magazine advertising campaigns (“Droid Does”) to widespread carrier exposure (every major carrier now offers an Android-powered device) Google has careened into the headlines without apology, humility, or lack of bombast. As if that weren’t enough, the fact that these new devices are authored and owned by 3rd party Google, Inc. introduces instant brand recognition, mass online exposure, and buying power to the package – all of which are intended to eschrew control and dominion from the existing (and antiquated) marriage of device + carrier. In the true spirit of open-source principles and in direct contrast to Apple’s model Google has made their source code (as they have done with most of their projects) publically available to any and all would-be developers. Apple on the other hand rigorously controls access to their development channels, imposes monetary obstacles to realized access, and strictly moderates submitted applications by what can only be described as “whim.” But what does this mean to the average consumer? It means that the Android Marketplace (Google’s version of the App Store) will operate on a truly open-market system wherein the users will have relatively unfiltered access to a wide range of applications and aspiring developers will have equal opportunities to make a splash in the mobile application market. This will result in more variety, competition, and access within the ever-growing Android Marketplace which as of this writing currently hosts more than 30,000 unique apps.

On a larger scale, it also means that there is now a legitimate challenger to Apple’s game-changing iPhone in Google. Furthermore, Google has taken the necessary actions to insure that it’s model is supported from the ground up and could, should it choose to, directly challenge even the service carriers themselves. These developments will surely result in lowered costs all around as Google, Apple, RIM, and others vie to compete with one another on an ever-evolving scale of technological prowess and awe. It’s impossible to predict who will emerge victorious, but one thing is for certain: it’s a great time to be a mobile consumer.