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