forked from geojeff/sproutcore-utils
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
initial commit of deploy, and Fabric deploy example
- Loading branch information
Jeff Pittman
committed
Jun 4, 2010
1 parent
3070ea8
commit 371a1cc
Showing
2 changed files
with
168 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
Introduction | ||
============ | ||
|
||
Sproutcore applications are deployed to a server from a local development area which | ||
typically consists of several directories, apps, for the applications, and frameworks, | ||
for dependencies of the applications. sc-build is run within the development area to | ||
produce minified javascript and css files to tmp/build/sc. The sc directory contains | ||
subdirectories for each app, storing the minified files under a "hash" directory. The | ||
hash is a long alphanumeric string, unique for each build, and doubling as the name | ||
of the containing directory. For example, after sc-build, you could have this for a | ||
Sproutcore app called myapp: | ||
|
||
../project/tmp/build/sc/myapp/myapp/en/bb2cb971f4f39dab7bdcfb7707ca1d24188a503/ | ||
|
||
and in this "hash" directory, you might find: | ||
|
||
* index.html | ||
* javascript.js | ||
|
||
For deployment, you would cd to ../project/tmp/build/, then: | ||
|
||
tar cvfz myapp.tgz sc | ||
|
||
You would then have a tarball with your app, as minified files, ready for deploying to | ||
a server. On the server, you would have a directory for the website associated with | ||
this app, and a subdirectory ready to hold the app. This subdirectory would probably | ||
be proxied to a URL, such that if you explode the tarball there, you would be ready | ||
to serve up the Sproutcore application. There is a last step though, for which you | ||
need to pay attention to where you explode the tarball, containing the hierarchy you | ||
see above (sc/myapp/myapp/en/bb2cb971f4f39dab7bdcfb7707ca1d24188a503) and files within | ||
it. One of the files inside the hash directory is index.html, which includes links to | ||
the hiearchy down to the hash directory files. This index.html needs to be copied to | ||
the proxied directory for your server environment, and it must be done so that the | ||
paths work. After you figure out how to arrange things, you'll have the repetive task | ||
of making a tarball, uploading it, exploding it in the right directory, and moving or | ||
symlinking the index.html to a proxied directory, each time you need to update the | ||
live app. If you have more than one Sproutcore app, this can get really tedious. So, | ||
deployment scripts are needed. | ||
|
||
Fabric | ||
------ | ||
|
||
Fabric is a Python library for ssh-type administrative tasks locally and on servers. | ||
Fabric is described at: http://docs.fabfile.org/0.9.1/. It works well for performing | ||
the steps described above, both on the local development system and the server. | ||
|
||
This fabfile.py | ||
--------------- | ||
|
||
Fabric uses a simple scheme, wherein tasks, written as Python methods, are stored in | ||
a fabfile.py file. You could put many such tash methods, along with other Python code, | ||
in a single fabfile.py. You run a task by typing 'fab task' on the command line, which | ||
would find and run the task() method in fabfile.py. | ||
|
||
In the example shown here, Fabric is used to deploy multiple Sproutcore apps to a single | ||
server, where johndoe has a normal user account with sudoer privledges. The deployment | ||
directory is a Django project directory, where Django apps live along with Sproutcore | ||
apps. nginx is used to proxy URLs to specific Django and Sproutcore app directories. | ||
For each Sproutcore app, our task is to deploy the fresh sc tarball and copy out | ||
index.html for each app to the proxied directory. | ||
|
||
This fabfile.py is put in a deploy directory at the top level of a Sproutcore project | ||
(in with Buildfile, apps, frameworks) on a local development machine. | ||
|
||
NOTE: This code assumes Python 2.6 or later. If you are using Python 2.5 or earlier, | ||
you'll need to import the new Python *with* statement in a special way described on | ||
the Fabric web site. | ||
|
||
1. Make a tarball of ../tmp/build/sc | ||
|
||
2. Send it up to the server, to johndoe's home dir | ||
|
||
3. Move it to the django project dir, where django apps and sc and sc apps live | ||
|
||
4. Move old sc out of the way | ||
|
||
5. Explode tarball to new sc | ||
|
||
6. For each Sproutcore app, copy out the index.html to the proxied dir | ||
|
||
You run it by issuing 'fab deploy'. fabric picks up the deploy() method defined here. | ||
You will see reports as the Fabric commands are executed, and will see errors as they | ||
happen. Once working, you might still have some admin tasks to do on the server | ||
before each deployment. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import os | ||
import datetime | ||
from fabric.api import * | ||
|
||
project_name = 'myproject' | ||
project_abbv = 'mp' | ||
server_path = '/path/to/django/dir' # (in the case of this example) | ||
|
||
apps_and_config = {'app_one' :{'proxy_prefix':'', 'latest_hash':''}, | ||
'app_two' :{'proxy_prefix':'', 'latest_hash':''}, | ||
'graph_app_one' :{'proxy_prefix':'graphs', 'latest_hash':''}, | ||
'graph_app_two' :{'proxy_prefix':'graphs', 'latest_hash':''}} | ||
|
||
def _app_dirs(): | ||
build_dir = '/local/path/to/sproutcore/tmp/build/sc/' | ||
dirs = os.listdir(build_dir) | ||
if 'sproutcore' in dirs: | ||
dirs.remove('sproutcore') | ||
return dirs | ||
|
||
def _find_hashes(): | ||
ok = True | ||
app_dirs = _app_dirs() | ||
for app in apps_and_config: | ||
if app not in app_dirs: | ||
print 'app NOT found:', app | ||
ok = False | ||
else: | ||
latest_hash = _latest_hash_dir(app) | ||
if len(latest_hash) == 0: | ||
print 'hash NOT found:', app | ||
ok = False | ||
else: | ||
apps_and_config[app]['latest_hash'] = latest_hash | ||
return ok | ||
|
||
def _latest_hash_dir(app): | ||
most_recent_mod = 1 | ||
build_dir = '/local/path/to/sproutcore/tmp/build/sc/%s/%s/en/' % (app, app) | ||
mod_times_and_hash_dirs = dict([(os.stat(os.path.join(build_dir, hash_dir)), hash_dir) for hash_dir in os.listdir(build_dir)]) | ||
most_recent_mod = max(mod_times_and_hash_dirs.keys()) | ||
if most_recent_mod > 1: | ||
return mod_times_and_hash_dirs[most_recent_mod] | ||
else: | ||
return '' | ||
|
||
def list_app_dirs(): | ||
print _app_dirs() | ||
|
||
def latest_hashes(): | ||
for app in _app_dirs(): | ||
files = run('ls %s' % _latest_hash_dir(app)).split('\n') | ||
print ' ', files | ||
|
||
def _local_pack(local_path_tgz): | ||
with cd('/local/path/to/sproutcore/tmp/build'): | ||
local('tar czf %s sc' % local_path_tgz) | ||
|
||
@hosts('johndoe@server') | ||
def deploy(): | ||
now_string = datetime.datetime.now().strftime("%Y_%m_%d_%H_%M") | ||
tgz = '%s_%s.tgz' % (project_abbv, now_string) | ||
local_path_tgz = '/tmp/%s' % tgz | ||
if _find_hashes(): | ||
_local_pack(local_path_tgz) | ||
put(local_path_tgz, '/home/johndoe/%s' % tgz) | ||
sudo('mv /home/johndoe/%s %s/%s' % (tgz, server_path, tgz), shell=False) | ||
sudo('mv %s/sc %s/sc_old_%s' % (server_path, server_path, now_string), shell=False) | ||
with cd(server_path): | ||
sudo('tar xvf %s' % tgz) | ||
sudo('chown -R nginx:nginx sc') | ||
for app in apps_and_config: | ||
latest_hash = apps_and_config[app]['latest_hash'] | ||
|
||
proxy_prefix = apps_and_config[app]['proxy_prefix'] | ||
proxy_path = os.path.join(proxy_prefix, app) | ||
hash_path = os.path.join('sc', app, app, 'en', latest_hash) | ||
|
||
old_index_html = 'index.html.old.%s' % now_string | ||
|
||
sudo('mv %s/index.html %s/%s' % (proxy_path, proxy_path, old_index_html)) | ||
sudo('cp %s/index.html %s/index.html' % (hash_path, proxy_path)) | ||
sudo('chown nginx:nginx %s/index.html' % proxy_path) | ||
|