2 Generic fabric deployment script.
3 Create a fabfile.py in the project and start it with:
5 from fnpdjango.deploy import *
7 Then set up some env properties:
8 project_name: slug-like project name
9 hosts: list of target host names
10 user: remote user name
11 app_path: where does the app go
12 services: list of tasks to run after deployment
15 from os.path import abspath, dirname, exists, join
16 from fabric.api import *
17 from fabric.contrib import files
18 from fabric.tasks import Task, execute
20 env.virtualenv = '/usr/bin/virtualenv'
27 Setup a fresh virtualenv as well as a few useful directories.
28 virtualenv should be already installed.
30 require('hosts', 'app_path', 'virtualenv')
32 run('mkdir -p %(app_path)s' % env, pty=True)
33 run('%(virtualenv)s %(app_path)s/ve' % env, pty=True)
34 run('mkdir -p %(app_path)s/releases %(app_path)s/packages %(app_path)s/log' % env, pty=True)
35 run('cd %(app_path)s/releases; ln -sfT . current; ln -sfT . previous' % env, pty=True)
37 print "Fill out db details in localsettings.py and run deploy."
43 Deploy the latest version of the site to the servers,
44 install any required third party modules,
45 install the virtual host and then restart the webserver
47 require('hosts', 'app_path')
50 env.release = time.strftime('%Y-%m-%dT%H%M')
54 install_requirements()
56 symlink_current_release()
64 Limited rollback capability. Simple loads the previously current
65 version of the code. Rolling back again will swap between the two.
66 Warning: this will almost certainly go wrong, it there were any migrations
69 require('hosts', 'app_path')
71 run('mv releases/current releases/_previous;', pty=True)
72 run('mv releases/previous releases/current;', pty=True)
73 run('mv releases/_previous releases/previous;', pty=True)
78 def deploy_version(version):
80 Loads the specified version.
81 Warning: this will almost certainly go wrong, it there were any migrations
84 "Specify a specific version to be made live"
85 require('hosts', 'app_path')
88 run('rm releases/previous; mv releases/current releases/previous;', pty=True)
89 run('ln -s %(version)s releases/current' % env, pty=True)
96 for service in env.services:
100 # =====================================================================
101 # = Helpers. These are called by other functions rather than directly =
102 # =====================================================================
103 class DebianGunicorn(Task):
104 def __init__(self, name):
105 super(Task, self).__init__()
109 print '>>> restart webserver using gunicorn-debian'
110 sudo('gunicorn-debian restart %s' % self.name, shell=False)
114 print '>>> restart webserver by touching WSGI'
116 run('touch %(app_path)s/%(project_name)s/wsgi.py' % env)
118 class Supervisord(Task):
119 def __init__(self, name):
120 super(Task, self).__init__()
124 print '>>> supervisord: restart %s' % self.name
125 sudo('supervisorctl restart %s' % self.name, shell=False)
130 run('[ -e %(app_path)s/ve ]' % env)
132 print "Environment isn't ready. Run `fab setup` first."
135 def upload_samples():
136 upload_localsettings_sample()
137 upload_nginx_sample()
138 upload_gunicorn_sample()
140 def upload_localsettings_sample():
141 "Fill out localsettings template and upload as a sample."
142 print '>>> upload localsettings template'
143 require('app_path', 'project_name')
144 template = '%(project_name)s/localsettings.py.template'
145 if not exists(template):
146 template = join(dirname(abspath(__file__)), 'templates/localsettings.py.template')
147 env.secret_key = '' # sth random
148 files.upload_template(template, '%(app_path)s/localsettings.py.sample' % env, env)
150 def upload_nginx_sample():
151 "Fill out nginx conf template and upload as a sample."
152 print '>>> upload nginx template'
153 require('app_path', 'project_name')
154 template = '%(project_name)s/nginx.template'
155 if not exists(template):
156 template = join(dirname(abspath(__file__)), 'templates/nginx.template')
157 files.upload_template(template, '%(app_path)s/nginx.sample' % env, env)
159 def upload_gunicorn_sample():
160 "Fill out gunicorn conf template and upload as a sample."
161 print '>>> upload gunicorn template'
162 require('app_path', 'project_name')
163 template = '%(project_name)s/gunicorn.template'
164 if not exists(template):
165 template = join(dirname(abspath(__file__)), 'templates/gunicorn.template')
166 files.upload_template(template, '%(app_path)s/gunicorn.sample' % env, env)
168 def upload_tar_from_git():
169 "Create an archive from the current Git branch and upload it"
170 print '>>> upload tar from git'
171 require('release', provided_by=[deploy])
173 local('git-archive-all.sh --format tar %(release)s.tar' % env)
174 local('gzip %(release)s.tar' % env)
175 run('mkdir -p %(app_path)s/releases/%(release)s' % env, pty=True)
176 run('mkdir -p %(app_path)s/packages' % env, pty=True)
177 put('%(release)s.tar.gz' % env, '%(app_path)s/packages/' % env)
178 run('cd %(app_path)s/releases/%(release)s && tar zxf ../../packages/%(release)s.tar.gz' % env, pty=True)
179 local('rm %(release)s.tar.gz' % env)
181 def install_requirements():
182 "Install the required packages from the requirements file using pip"
183 print '>>> install requirements'
184 require('release', provided_by=[deploy])
186 run('cd %(app_path)s; ve/bin/pip install -r %(app_path)s/releases/%(release)s/requirements.txt' % env, pty=True)
188 def copy_localsettings():
189 "Copy localsettings.py from root directory to release directory (if this file exists)"
190 print ">>> copy localsettings"
191 require('release', provided_by=[deploy])
192 require('app_path', 'project_name')
194 with settings(warn_only=True):
195 run('cp %(app_path)s/localsettings.py %(app_path)s/releases/%(release)s/%(project_name)s' % env)
197 def symlink_current_release():
198 "Symlink our current release"
199 print '>>> symlink current release'
200 require('release', provided_by=[deploy])
202 with cd(env.app_path):
203 run('rm releases/previous; mv releases/current releases/previous')
204 run('ln -s %(release)s releases/current' % env)
207 "Update the database"
209 require('app_path', 'project_name')
210 with cd('%(app_path)s/releases/current' % env):
211 run('%(app_path)s/ve/bin/python manage.py syncdb --noinput' % env, pty=True)
212 run('%(app_path)s/ve/bin/python manage.py migrate' % env, pty=True)
215 """Collect static files"""
216 print '>>> collectstatic'
217 require('app_path', 'project_name')
218 with cd('%(app_path)s/releases/current' % env):
219 run('%(app_path)s/ve/bin/python manage.py collectstatic --noinput' % env, pty=True)