c4965de618a9d143b0ad8204ecf72ddf5f78f70c
[koed-quiz.git] / fabfile.py
1 from __future__ import with_statement # needed for python 2.5
2 from fabric.api import *
3 from fabric.contrib import files
4
5 import os
6
7
8 # ==========
9 # = Config =
10 # ==========
11 # Globals
12 env.project_name = 'koedquiz'
13 env.use_south = True
14
15 # Servers
16 def localhost():
17     """SSH to localhost (for debugging).
18
19     This will deploy to `test-deployment` in the project dir.
20
21     """
22     import os.path
23     from getpass import getuser
24
25     env.hosts = ['localhost']
26     env.user = getuser()
27     env.path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test-deployment')
28     env.virtualenv = '/usr/bin/virtualenv'
29     # This goes to VHost configuration
30     env.server_name = 'koedquiz.example.com'
31     env.server_admin = 'koedquiz <koedquiz@koedquiz.example.com>'
32     # /var/log/apache2/* logs
33     env.access_log = 'koedquiz.log'
34     env.error_log = 'koedquiz-errors.log'
35
36
37 # add additional servers here
38
39
40 servers = [localhost]
41
42 # =========
43 # = Tasks =
44 # =========
45 def test():
46     "Run the test suite and bail out if it fails"
47     require('hosts', 'path', provided_by=servers)
48     require('python', provided_by=[find_python])
49     result = run('cd %(path)s/%(project_name)s; %(python)s manage.py test' % env)
50
51 def setup():
52     """
53     Setup a fresh virtualenv as well as a few useful directories, then run
54     a full deployment. virtualenv with pip should be already installed.
55     """
56     require('hosts', 'path', 'virtualenv', provided_by=servers)
57
58     run('mkdir -p %(path)s; cd %(path)s; %(virtualenv)s ve;' % env, pty=True)
59     run('cd %(path)s; mkdir releases; mkdir packages;' % env, pty=True)
60     run('cd %(path)s/releases; ln -s . current; ln -s . previous' % env, pty=True)
61     upload_default_localsettings()
62     deploy()
63
64 def deploy():
65     """
66     Deploy the latest version of the site to the servers,
67     install any required third party modules,
68     install the virtual host and then restart the webserver
69     """
70
71     import time
72     env.release = time.strftime('%Y-%m-%dT%H%M')
73
74     upload_tar_from_git()
75     find_python()
76     upload_wsgi_script()
77     upload_vhost_sample()
78     install_requirements()
79     copy_localsettings()
80     symlink_current_release()
81     collectstatic()
82     migrate()
83     restart_webserver()
84
85 def deploy_version(version):
86     "Specify a specific version to be made live"
87     require('hosts', 'path', provided_by=servers)
88     env.version = version
89     with cd(env.path):
90         run('rm releases/previous; mv releases/current releases/previous;', pty=True)
91         run('ln -s %(version)s releases/current' % env, pty=True)
92     restart_webserver()
93
94 def rollback():
95     """
96     Limited rollback capability. Simple loads the previously current
97     version of the code. Rolling back again will swap between the two.
98     """
99     require('hosts', 'path', provided_by=servers)
100     with cd(env.path):
101         run('mv releases/current releases/_previous;', pty=True)
102         run('mv releases/previous releases/current;', pty=True)
103         run('mv releases/_previous releases/previous;', pty=True)
104     restart_webserver()
105
106
107 # =====================================================================
108 # = Helpers. These are called by other functions rather than directly =
109 # =====================================================================
110 def upload_tar_from_git():
111     "Create an archive from the current Git branch and upload it"
112     print '>>> upload tar from git'
113     require('path', provided_by=servers)
114     require('release', provided_by=[deploy])
115     local('/bin/bash lib/git-archive-all.sh --format tar %(release)s.tar' % env)
116     local('gzip %(release)s.tar' % env)
117     run('mkdir -p %(path)s/releases/%(release)s' % env, pty=True)
118     run('mkdir -p %(path)s/packages' % env, pty=True)
119     put('%(release)s.tar.gz' % env, '%(path)s/packages/' % env)
120     run('cd %(path)s/releases/%(release)s && tar zxf ../../packages/%(release)s.tar.gz' % env, pty=True)
121     local('rm %(release)s.tar.gz' % env)
122
123 def find_python():
124     "Finds where virtualenv Python stuff is"
125     print ">>> find Python paths"
126     require('path', provided_by=servers)
127     env.python = '%(path)s/ve/bin/python' % env
128     env.pip = '%(path)s/ve/bin/pip' % env
129     env.site_packages = run('%(python)s -c "from distutils.sysconfig import get_python_lib; print get_python_lib()"' % env)
130
131 def upload_vhost_sample():
132     "Create and upload Apache virtual host configuration sample"
133     print ">>> upload vhost sample"
134     require('path', 'project_name', 'user', provided_by=servers)
135     require('access_log', 'error_log', 'server_admin', 'server_name', provided_by=servers)
136     require('site_packages', provided_by=[find_python])
137     files.upload_template('%(project_name)s.vhost.template' % env, '%(path)s/%(project_name)s.vhost' % env, context=env)
138
139 def upload_wsgi_script():
140     "Create and upload a wsgi script sample"
141     print ">>> upload wsgi script sample"
142     require('path', 'project_name', provided_by=servers)
143     require('python', 'site_packages', provided_by=[find_python])
144     files.upload_template('%(project_name)s.wsgi.template' % env, '%(path)s/%(project_name)s.wsgi' % env, context=env)
145     run('chmod ug+x %(path)s/%(project_name)s.wsgi' % env)
146
147 def install_requirements():
148     "Install the required packages from the requirements file using pip"
149     print '>>> install requirements'
150     require('path', provided_by=servers)
151     require('release', provided_by=[deploy])
152     require('pip', provided_by=[find_python])
153     run('%(pip)s install -r %(path)s/releases/%(release)s/requirements.txt' % env, pty=True)
154
155 def secret_key():
156     """Generates a new SECRET_KEY."""
157     from random import Random
158     import string
159
160     r = Random()
161     return "".join(r.choice(string.printable) for i in range(64))
162
163 def upload_default_localsettings():
164     "Uploads localsettings.py with media paths and stuff"
165     print ">>> upload default localsettings.py"
166     require('path', provided_by=servers)
167
168     env.secret_key = secret_key()
169     files.upload_template('%(project_name)s/localsettings.py.template' % env, '%(path)s/localsettings.py' % env, context=env)
170
171 def copy_localsettings():
172     "Copy localsettings.py from root directory to release directory (if this file exists)"
173     print ">>> copy localsettings"
174     require('path', 'project_name', provided_by=servers)
175     require('release', provided_by=[deploy])
176
177     with settings(warn_only=True):
178         run('cp %(path)s/localsettings.py %(path)s/releases/%(release)s/%(project_name)s' % env)
179
180 def symlink_current_release():
181     "Symlink our current release"
182     print '>>> symlink current release'
183     require('path', provided_by=servers)
184     require('release', provided_by=[deploy])
185     with cd(env.path):
186         run('rm releases/previous; mv releases/current releases/previous')
187         run('ln -s %(release)s releases/current' % env)
188
189 def collectstatic():
190     """Runs collectstatic management command from Django staticfiles."""
191     print '>>> collectstatic'
192     require('path', 'project_name', provided_by=servers)
193     require('python', provided_by=[find_python])
194     with cd('%(path)s/releases/current/%(project_name)s' % env):
195         run('%(python)s manage.py collectstatic --noinput' % env, pty=True)
196
197 def migrate():
198     "Update the database"
199     print '>>> migrate'
200     require('path', 'project_name', provided_by=servers)
201     require('python', provided_by=[find_python])
202     with cd('%(path)s/releases/current/%(project_name)s' % env):
203         run('%(python)s manage.py syncdb --noinput' % env, pty=True)
204         if env.use_south:
205             run('%(python)s manage.py migrate' % env, pty=True)
206
207 def restart_webserver():
208     "Restart the web server"
209     print '>>> restart webserver'
210     require('path', 'project_name', provided_by=servers)
211     run('touch %(path)s/%(project_name)s.wsgi' % env)