Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to minify & version js/css on Google App Engine?

Since webassets won't work on GAE to compress a js/css on the fly, it seems the best approach is to do it upon deployment.

After a lot of googling I came up with the script below to achieve this.

At first I thought the best would be to leave the javascript path in base.html as it is and simply compress the css/js.

cssmin compresses the css and overwrites the original. However closure doesn't allow overwriting the original and this concept fails already.

The second problem is, even if I got closure overwriting the original file, caching will be a problem. For this reason each deployment of minified css/js should come with a random number in file name, so that the new versions are actually picked up after a new deployment. With the concept I came up with, this won't be possible though.

Hence the only way to achieve this would be modifying the base.html with sed or something.

Before I reinvent the wheel, is there a better approach to do this? Many thanks

import sys, os
import cssmin

def main():
    if len(sys.argv) == 1:
        return

    appId = sys.argv[1]
    print "appId", appId

    cmd = r'java -jar compiler.jar --js=/src/application/static/f11/f11.js --js_output_file=/src/application/static/f11/f11.min.js'
    os.system(cmd)

    output = cssmin.cssmin(open('/src/application/static/f11/f11.css').read())
    f = open('/src/application/static/f11/f11.css','w')
    f.write(output)    

    # Perform appcfg.py to update GAE server
    cmd = r'"\google_appengine\appcfg.py"'
    os.system(cmd + " update . " + " -A %s"%appId)


if __name__ == "__main__":
    main()
like image 919
Houman Avatar asked Jun 20 '13 17:06

Houman


1 Answers

You should do a one-time detection on instance startup that sets some global vars depending on if your app is running on the dev server. You generate URLs you need to your assets based on that, and have a list of versions for each asset.

python example (full context):

JQUERY_VERSION = '1.7.2'
JQUERY_UI_VERSION = '1.8.20'
ANGULAR_VERSION = '1.0.2'

if os.environ['SERVER_SOFTWARE'].startswith('Google'):
    JQUERY_URL = "//ajax.googleapis.com/ajax/libs/jquery/%(version)s/jquery.min.js" %{ 'version': JQUERY_VERSION }
    JQUERY_UI_URL = "//ajax.googleapis.com/ajax/libs/jqueryui/%(version)s/jquery-ui.min.js" %{ 'version': JQUERY_UI_VERSION }
    JQUERY_UI_CSS_URL = "//ajax.googleapis.com/ajax/libs/jqueryui/%(version)s/themes/base/jquery.ui.all.css" %{ 'version': JQUERY_UI_VERSION }
    ANGULAR_URL = "//ajax.googleapis.com/ajax/libs/angularjs/%(version)s/angular.min.js" %{ 'version': ANGULAR_VERSION }
else:
    JQUERY_URL = "/static/js/jquery-%(version)s.min.js" %{ 'version': JQUERY_VERSION }
    JQUERY_UI_URL = "/static/js/jquery-ui-%(version)s.min.js" %{ 'version': JQUERY_UI_VERSION }
    JQUERY_UI_CSS_URL = "/static/css/jquery-ui/jquery-ui-%(version)s.css" %{ 'version': JQUERY_UI_VERSION }
    ANGULAR_URL = "/static/js/angular-%(version)s.min.js" %{ 'version': ANGULAR_VERSION }

go example (full context):

func init() {
    angular_ver := "1.0.5"
    bootstrap_ver := "2.3.1"
    jquery_ver := "1.9.1"

    if appengine.IsDevAppServer() {
        Angular = fmt.Sprintf("/static/js/angular-%v.js", angular_ver)
        BootstrapCss = fmt.Sprintf("/static/css/bootstrap-%v.css", bootstrap_ver)
        BootstrapJs = fmt.Sprintf("/static/js/bootstrap-%v.js", bootstrap_ver)
        Jquery = fmt.Sprintf("/static/js/jquery-%v.js", jquery_ver)
    } else {
        Angular = fmt.Sprintf("//ajax.googleapis.com/ajax/libs/angularjs/%v/angular.min.js", angular_ver)
        BootstrapCss = fmt.Sprintf("//netdna.bootstrapcdn.com/twitter-bootstrap/%v/css/bootstrap-combined.min.css", bootstrap_ver)
        BootstrapJs = fmt.Sprintf("//netdna.bootstrapcdn.com/twitter-bootstrap/%v/js/bootstrap.min.js", bootstrap_ver)
        Jquery = fmt.Sprintf("//ajax.googleapis.com/ajax/libs/jquery/%v/jquery.min.js", jquery_ver)
    }
}

If you have a deploy stript that minifies your local (i.e., non CDN) content, run that here, and use the method above, but with a local url with a .min extension.

like image 103
mjibson Avatar answered Oct 16 '22 00:10

mjibson