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 fabric.api import *
16 from os.path import abspath, dirname, exists, join
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' % 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'
111 sudo('gunicorn-debian restart %s' % self.site_name, shell=False)
115 print '>>> restart webserver by touching WSGI'
117 run('touch %(app_path)s/%(project_name)s/wsgi.py' % env)
119 class Supervisord(Task):
120 def __init__(self, name):
121 super(Task, self).__init__()
125 print '>>> supervisord: restart %s' % self.name
127 sudo('supervisorctl restart %s' % self.name, shell=False)
132 run('[ -e %(app_path)s/ve ]' % env)
134 print "Environment isn't ready. Run `fab setup` first."
137 def upload_samples():
138 upload_localsettings_sample()
139 upload_nginx_sample()
140 upload_gunicorn_sample()
142 def upload_localsettings_sample():
143 "Fill out localsettings template and upload as a sample."
144 print '>>> upload localsettings template'
145 require('app_path', 'project_name')
146 template = '%(project_name)s/localsettings.py.template'
147 if not exists(template):
148 template = join(dirname(abspath(__file__)), 'templates/localsettings.py.template')
149 env.secret_key = '' # sth random
150 files.upload_template(template, '%(app_path)s/localsettings.py.sample' % env, env)
152 def upload_nginx_sample():
153 "Fill out nginx conf template and upload as a sample."
154 print '>>> upload nginx template'
155 require('app_path', 'project_name')
156 template = '%(project_name)s/nginx.template'
157 if not exists(template):
158 template = join(dirname(abspath(__file__)), 'templates/nginx.template')
159 files.upload_template(template, '%(app_path)s/nginx.sample' % env, env)
161 def upload_gunicorn_sample():
162 "Fill out gunicorn conf template and upload as a sample."
163 print '>>> upload gunicorn template'
164 require('app_path', 'project_name')
165 template = '%(project_name)s/gunicorn.template'
166 if not exists(template):
167 template = join(dirname(abspath(__file__)), 'templates/gunicorn.template')
168 files.upload_template(template, '%(app_path)s/gunicorn.sample' % env, env)
170 def upload_tar_from_git():
171 "Create an archive from the current Git branch and upload it"
172 print '>>> upload tar from git'
173 require('release', provided_by=[deploy])
175 local('git-archive-all.sh --format tar %(release)s.tar' % env)
176 local('gzip %(release)s.tar' % env)
177 run('mkdir -p %(app_path)s/releases/%(release)s' % env, pty=True)
178 run('mkdir -p %(app_path)s/packages' % env, pty=True)
179 put('%(release)s.tar.gz' % env, '%(app_path)s/packages/' % env)
180 run('cd %(app_path)s/releases/%(release)s && tar zxf ../../packages/%(release)s.tar.gz' % env, pty=True)
181 local('rm %(release)s.tar.gz' % env)
183 def install_requirements():
184 "Install the required packages from the requirements file using pip"
185 print '>>> install requirements'
186 require('release', provided_by=[deploy])
188 run('cd %(app_path)s; ve/bin/pip install -r %(app_path)s/releases/%(release)s/requirements.txt' % env, pty=True)
190 def copy_localsettings():
191 "Copy localsettings.py from root directory to release directory (if this file exists)"
192 print ">>> copy localsettings"
193 require('release', provided_by=[deploy])
194 require('app_path', 'project_name')
196 with settings(warn_only=True):
197 run('cp %(app_path)s/localsettings.py %(app_path)s/releases/%(release)s/%(project_name)s' % env)
199 def symlink_current_release():
200 "Symlink our current release"
201 print '>>> symlink current release'
202 require('release', provided_by=[deploy])
205 run('rm releases/previous; mv releases/current releases/previous')
206 run('ln -s %(release)s releases/current' % env)
209 "Update the database"
211 require('app_path', 'project_name')
212 with cd('%(app_path)s/releases/current/%(project_name)s' % env):
213 run('%(app_path)s/ve/bin/python manage.py syncdb --noinput' % env, pty=True)
214 run('%(app_path)s/ve/bin/python manage.py migrate' % env, pty=True)
217 """Collect static files"""
218 print '>>> collectstatic'
219 require('app_path', 'project_name')
220 with cd('%(app_path)s/releases/current/%(project_name)s' % env):
221 run('%(app_path)s/ve/bin/python manage.py collectstatic --noinput' % env, pty=True)