@@ 0,0 1,32 @@
+Copyright (c) 2010, Peter Sanchez <petersanchez@gmail.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the
+following conditions are met:
+
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials
+ provided with the distribution.
+
+ * Neither the name of Peter Sanchez nor the names of its
+ contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ 0,0 1,142 @@
+fabric-deploy-django
+------------------
+
+Easily reusable fabfile that can be used with the fabric application
+to quickly deploy a Django project using Apache, mod_wsgi, virtualenv
+and pip.
+
+
+Dependencies
+------------
+
+You will need to have fabric installed to use this fabfile. Install it
+it like so:
+
+$ sudo pip install fabric
+
+It was written for Python 2.6+
+
+If you use a Python version less than 2.6 then please refer to this
+URL:
+
+http://docs.fabfile.org/0.9.2/#documentation
+
+You will need to add the following to your fabfile.py:
+
+from __future__ import with_statement
+
+
+Install
+-------
+
+Simply copy the fabfile.py to the root of your project directory and
+edit the fabfile.py to fit your project.
+
+Project structure should look like so:
+
+myproject/
+ apache/
+ wsgi_handler.py
+ myproject.conf
+ requirements.txt
+ fabfile.py
+ myproject/
+ __init__.py
+ urls.py
+ settings.py
+
+
+Configure
+---------
+
+Edit the following globals:
+
+
+env.project_name = 'PROJECT_NAME'
+
+This is the name the project (ie, "myproject" above in the structure example)
+
+
+env.run_south_migrate = True
+
+Run 'python manage.py migrate' after syncdb is run.
+
+
+env.copy_base_settings_local = False
+
+If true, the script will look for a file named myproject/base_settings_local.py
+and copy it to myproject/settings_local.py
+
+
+env.num_releases = 7
+
+The number of old releases to leave on the deploy destination.
+
+
+Edit the 3 deploy defining functions in the fabfile (local, staging, prod):
+
+
+env.hosts = ['localhost']
+
+Host(s) that the commands should be run on
+
+
+env.path = '/path/to/envs/myproject'
+
+Path for this project to be deployed to.
+
+
+env.user = 'yourusername'
+
+The username to run the commands as
+
+
+env.run_type = 'local'
+
+If this is set to 'local' then the internal function _is_local() will evaluate
+to True. This means that the deploy is actually being run on the LOCAL machine.
+This is mostly used for testing the deploy process. Anything other than
+'local' will mean that the deploy will be run on whatever hosts are set in
+"env.hosts"
+
+
+env.repo_path = '/path/to/local/repo/myproject'
+
+Path (local FS or URL) to the repository to clone (or pull from) during the
+deploy.
+
+
+env.repo_rev = 'tip'
+
+The revision to use when pulling/cloning from the repository
+(set in env.repo_path above)
+
+
+Use
+---
+
+Here is a basic deploy to the LOCAL machine:
+
+$ fab local setup
+
+Or say to the production environment:
+
+$ fab prod setup
+
+
+TODO
+----
+Create a more through install and use docs.
+
+Auto import __future__ if using Python < 2.6
+
+Make various functions more loosely coupled to the tools currently.
+(ie, easy to switch from deploying with hg to git or svn, etc.)
+
+
+Copyright & Warranty
+--------------------
+All documentation, libraries, and sample code are
+Copyright 2010 Peter Sanchez <petersanchez@gmail.com>. The library and
+sample code are made available to you under the terms of the BSD license
+which is contained in the included file, BSD-LICENSE.
@@ 0,0 1,296 @@
+'''
+Original idea from:
+
+http://gist.github.com/571155
+
+Hacked it to basically be completely original.
+
+'''
+import os
+from fabric.api import *
+
+
+# globals
+env.project_name = 'PROJECT_NAME'
+env.run_south_migrate = True
+env.copy_base_settings_local = False
+env.num_releases = 7
+
+
+# Environments
+def _is_local():
+ require('run_type', provided_by=[local, staging, prod])
+ return env.run_type == 'local'
+
+
+def _get_callable(default=run):
+ 'Return local or default (run) based on hosts variable'
+ require('run_type', provided_by=[local, staging, prod])
+ def _local(cmd, *args, **kwargs):
+ return local(cmd)
+ return _local if _is_local() else default
+
+
+def _get_cmds():
+ 'Return run, cd and sudo commands.'
+ return (
+ _get_callable(),
+ _get_callable(default=sudo),
+ )
+
+
+def localdev():
+ 'Use the local machine'
+ env.hosts = ['localhost']
+ env.path = '/path/to/environments/PROJECT_NAME'
+ env.user = 'username'
+ env.run_type = 'local'
+ env.repo_path = '/path/to/local/repository'
+ env.repo_rev = 'tip'
+ env.copy_base_settings_local = True
+
+
+def staging():
+ 'Use the staging environment'
+ pass
+
+
+def prod():
+ 'Use the production environment'
+ env.hosts = ['yourproject.yourdomain.com']
+ env.path = '/path/to/environments/PROJECT_NAME'
+ env.user = 'username'
+ env.run_type = 'prod'
+ env.repo_path = 'http://bitbucket.org/someuser/yourproject'
+ env.repo_rev = 'tip'
+
+
+def set_repo_revision(rev='tip'):
+ env.repo_rev = rev
+
+
+def set_num_releases(amount=7):
+ env.num_releases = amount
+
+
+# Tasks
+def test():
+ 'Run the test suite and bail out if it fails'
+ _run, _sudo = _get_cmds()
+
+ with cd(env.project_name):
+ _run('python manage.py test')
+
+
+def setup():
+ '''
+ Setup a fresh virtualenv as well as a few useful directories, then run
+ a full deployment
+ '''
+ require('hosts', provided_by=[localdev, staging, prod])
+ require('path')
+ _run, _sudo = _get_cmds()
+
+ import time
+ env.release = time.strftime('%Y%m%d%H%M%S')
+
+ lpath = '%(path)s/releases/%(release)s' % env
+ _run('mkdir -p %s' % lpath)
+ with cd(lpath):
+ _run('virtualenv --no-site-packages .')
+ _run('mkdir -p shared packages')
+ deploy()
+
+
+def soft_update():
+ 'Just update codebase. Do not do a full deploy'
+ require('hosts', provided_by=[localdev, staging, prod])
+ require('path')
+
+ checkout_code_repo(update=True)
+ touch_wsgi_handler()
+
+
+def deploy():
+ '''
+ Deploy the latest version of the site to the servers, install any
+ required third party modules, install the virtual host and
+ then restart the webserver
+ '''
+ require('hosts', provided_by=[localdev, staging, prod])
+ require('path')
+
+ checkout_code_repo()
+ install_requirements()
+ migrate()
+ install_site()
+ symlink_current_release()
+ restart_webserver()
+ remove_oldest_release()
+
+
+def show_versions():
+ 'List all deployed versions'
+ require('path')
+ _run, _sudo = _get_cmds()
+
+ with cd('%(path)s/releases' % env):
+ print _run('ls -xt')
+
+
+def rollback_version(version):
+ 'Specify a specific version to be made live'
+ require('hosts', provided_by=[localdev, staging, prod])
+ require('path')
+ _run, _sudo = _get_cmds()
+
+ env.version = version
+ with cd('%(path)s' % env):
+ _run('ln -nfs releases/%(version)s current' % env)
+ restart_webserver()
+
+
+def rollback():
+ 'Simple GENERIC rollback. Symlink to the second most recent release'
+ require('hosts', provided_by=[localdev, staging, prod])
+ require('path')
+ _run, _sudo = _get_cmds()
+
+ releases = _get_releases_list()
+ release = releases[-2]
+ _run('ln -nfs releases/%s current' % release)
+ restart_webserver()
+
+
+def fail_cleanup():
+ '''
+ If your VERY LAST deploy failed, call this to clean up the
+ mess left behind. It will only remove the most recent
+ release directory. BE CAREFUL USING THIS.
+ '''
+ require('hosts', provided_by=[localdev, staging, prod])
+ require('path')
+ _run, _sudo = _get_cmds()
+
+ releases = _get_releases_list()
+ release = releases[-1]
+ with cd('%(path)s/releases' % env):
+ _run('rm -rf %s' % release)
+
+
+# Helpers. These are called by other functions rather than directly
+def _get_releases_list():
+ 'Returns sorted list of all current releases'
+ require('hosts', provided_by=[localdev, staging, prod])
+ require('path')
+ _run, _sudo = _get_cmds()
+
+ with cd('%(path)s/releases' % env):
+ releases = _run('ls -xt')
+ releases = [x.replace('/', '') for x in releases.split()]
+ return sorted(releases)
+
+
+def checkout_code_repo(update=False):
+ 'Create an archive from the current Git master branch and upload it'
+ require('repo_path', provided_by=[localdev, staging, prod])
+ require('repo_rev', provided_by=[localdev, staging, prod])
+ _run, _sudo = _get_cmds()
+
+ if update:
+ lpath = '%(path)s/current/%(project_name)s' % env
+ with cd(lpath):
+ _run('hg pull -r %(repo_rev)s -u' % env)
+ else:
+ require('release', provided_by=[deploy, setup])
+ lpath = '%(path)s/releases/%(release)s' % env
+ with cd(lpath):
+ cmd = 'hg clone -r '
+ cmd += '%(repo_rev)s %(repo_path)s %(project_name)s' % env
+ _run(cmd)
+
+ if env.copy_base_settings_local:
+ lpath = os.path.join(lpath, '%(project_name)s/%(project_name)s' % env)
+ with cd(lpath):
+ with settings(warn_only=True):
+ _run('cp base_settings_local.py settings_local.py')
+
+
+def install_site():
+ 'Add the virtualhost file to apache'
+ require('release', provided_by=[deploy, setup])
+ _run, _sudo = _get_cmds()
+
+ if not _is_local():
+ with cd('%(path)s/releases/%(release)s' % env):
+ cmd = 'cp %(project_name)s/apache/%(project_name)s.conf ' % env + \
+ '/etc/apache2/conf.d/'
+ _sudo(cmd)
+
+
+def install_requirements():
+ 'Install the required packages from the requirements file using pip'
+ require('release', provided_by=[deploy, setup])
+ _run, _sudo = _get_cmds()
+
+ with cd('%(path)s/releases/%(release)s' % env):
+ cmd = './bin/pip install -E . '
+ cmd += '-r ./%(project_name)s/requirements.txt' % env
+ _run(cmd)
+
+
+def symlink_current_release():
+ 'Symlink our current release'
+ require('release', provided_by=[deploy, setup])
+ _run, _sudo = _get_cmds()
+
+ with cd('%(path)s' % env):
+ _run('ln -nfs releases/%(release)s current' % env)
+
+
+def migrate():
+ 'Update the database'
+ require('project_name')
+ require('release', provided_by=[deploy, setup])
+ _run, _sudo = _get_cmds()
+
+ p = '%(path)s/releases/%(release)s/%(project_name)s/%(project_name)s' % env
+ with cd(p):
+ _run('../../bin/python manage.py syncdb --noinput')
+ if env.run_south_migrate:
+ _run('../../bin/python manage.py migrate')
+
+
+def restart_webserver():
+ 'Restart the web server'
+ _run, _sudo = _get_cmds()
+
+ if not _is_local():
+ _sudo('service apache2 restart')
+
+
+def touch_wsgi_handler():
+ 'Touch the mod_wsgi wsgi_handler.py file'
+ require('project_name')
+ require('hosts', provided_by=[localdev, staging, prod])
+ _run, _sudo = _get_cmds()
+
+ with cd('%(path)s/current/%(project_name)s/apache' % env):
+ _run('touch wsgi_handler.py')
+
+
+def remove_oldest_release():
+ '''
+ Remove oldest releases past the ammount passed
+ in with the "allow" variable. Default 7.
+ '''
+ require('hosts', provided_by=[localdev, staging, prod])
+ require('num_releases')
+ require('path')
+ _run, _sudo = _get_cmds()
+
+ releases = _get_releases_list()
+ with cd('%(path)s/releases' % env):
+ while len(releases) > env.num_releases:
+ release = releases.pop(0)
+ _run('rm -rf %s' % release)