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