Merge branch 'master' into sunburnt
[wolnelektury.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 from fabric.context_managers import path
5
6 import os
7
8
9 # ==========
10 # = Config =
11 # ==========
12 # Globals
13 env.project_name = 'wolnelektury'
14 env.use_south = True
15
16 # Servers
17 def staging():
18     """Use staging server"""
19     env.hosts = ['stigma.nowoczesnapolska.org.pl:2222']
20     env.user = 'platforma'
21     env.path = '/var/services/wolnelektury'
22     env.python = '/usr/bin/python'
23     env.virtualenv = '/usr/bin/virtualenv'
24     env.pip = '/usr/bin/pip'
25
26 def production():
27     """Use production server"""
28     env.hosts = ['wolnelektury.pl']
29     env.user = 'lektury'
30     env.path = '/srv/wolnelektury.pl'
31     env.python = '/usr/bin/python'
32     env.virtualenv = '/usr/bin/virtualenv'
33     env.pip = 've/bin/pip'
34     env.restart_webserver = restart_gunicorn_debian
35
36 # =========
37 # = Tasks =
38 # =========
39 def test():
40     "Run the test suite and bail out if it fails"
41     require('hosts', 'path', provided_by=[staging, production])
42     result = run('cd %(path)s/%(project_name)s; %(python)s manage.py test' % env)
43
44 def setup():
45     """
46     Setup a fresh virtualenv as well as a few useful directories, then run
47     a full deployment. virtualenv and pip should be already installed.
48     """
49     require('hosts', 'path', provided_by=[staging, production])
50
51     run('mkdir -p %(path)s; cd %(path)s; %(virtualenv)s .;' % env, pty=True)
52     run('cd %(path)s; mkdir releases; mkdir shared; mkdir packages;' % env, pty=True)
53     run('cd %(path)s/releases; ln -s . current; ln -s . previous' % env, pty=True)
54     deploy()
55
56 def deploy():
57     """
58     Deploy the latest version of the site to the servers,
59     install any required third party modules,
60     install the virtual host and then restart the webserver
61     """
62     require('hosts', 'path', provided_by=[staging, production])
63
64     import time
65     env.release = time.strftime('%Y-%m-%dT%H%M')
66
67     upload_tar_from_git()
68     upload_wsgi_script()
69     upload_vhost_sample()
70     upload_celery_conf()
71     install_requirements()
72     copy_localsettings()
73     symlink_current_release()
74     migrate()
75     collectstatic()
76     restart_webserver()
77     restart_celery()
78
79 def deploy_version(version):
80     "Specify a specific version to be made live"
81     require('hosts', 'path', provided_by=[localhost,webserver])
82     env.version = version
83     with cd(env.path):
84         run('rm releases/previous; mv releases/current releases/previous;', pty=True)
85         run('ln -s %(version)s releases/current' % env, pty=True)
86     restart_webserver()
87     restart_celery()
88
89 def rollback():
90     """
91     Limited rollback capability. Simple loads the previously current
92     version of the code. Rolling back again will swap between the two.
93     """
94     require('hosts', provided_by=[staging, production])
95     require('path')
96     with cd(env.path):
97         run('mv releases/current releases/_previous;', pty=True)
98         run('mv releases/previous releases/current;', pty=True)
99         run('mv releases/_previous releases/previous;', pty=True)
100     restart_webserver()
101     restart_celery()
102
103
104 # =====================================================================
105 # = Helpers. These are called by other functions rather than directly =
106 # =====================================================================
107 def upload_tar_from_git():
108     "Create an archive from the current Git branch and upload it"
109     print '>>> upload tar from git'
110     require('release', provided_by=[deploy])
111     local('git-archive-all.sh --format tar %(release)s.tar' % env)
112     local('gzip %(release)s.tar' % env)
113     run('mkdir -p %(path)s/releases/%(release)s' % env, pty=True)
114     run('mkdir -p %(path)s/packages' % env, pty=True)
115     put('%(release)s.tar.gz' % env, '%(path)s/packages/' % env)
116     run('cd %(path)s/releases/%(release)s && tar zxf ../../packages/%(release)s.tar.gz' % env, pty=True)
117     local('rm %(release)s.tar.gz' % env)
118
119 def upload_vhost_sample():
120     "Create and upload Apache virtual host configuration sample"
121     print ">>> upload vhost sample"
122     files.upload_template('%(project_name)s.vhost.template' % env, '%(path)s/%(project_name)s.vhost.sample' % env, context=env)
123
124 def upload_wsgi_script():
125     "Create and upload a wsgi script sample"
126     print ">>> upload wsgi script sample"
127     files.upload_template('%(project_name)s.wsgi.template' % env, '%(path)s/%(project_name)s.wsgi' % env, context=env)
128     run('chmod ug+x %(path)s/%(project_name)s.wsgi' % env)
129
130 def upload_celery_conf():
131     "Create and upload a Celery conf for supervisord"
132     print ">>> upload celery supervisord conf"
133     files.upload_template('%(project_name)s-celery.conf.template' % env, '%(path)s/%(project_name)s-celery.conf' % env, context=env)
134     run('chmod ug+x %(path)s/%(project_name)s-celery.conf' % env)
135
136 def install_requirements():
137     "Install the required packages from the requirements file using pip"
138     print '>>> install requirements'
139     require('release', provided_by=[deploy])
140     run('cd %(path)s; %(pip)s install -E ve -r %(path)s/releases/%(release)s/requirements.txt' % env, pty=True)
141
142 def copy_localsettings():
143     "Copy localsettings.py from root directory to release directory (if this file exists)"
144     print ">>> copy localsettings"
145     require('release', provided_by=[deploy])
146     require('path', provided_by=[staging, production])
147
148     with settings(warn_only=True):
149         run('cp %(path)s/localsettings.py %(path)s/releases/%(release)s/%(project_name)s' % env)
150
151 def symlink_current_release():
152     "Symlink our current release"
153     print '>>> symlink current release'
154     require('release', provided_by=[deploy])
155     require('path', provided_by=[staging, production])
156     with cd(env.path):
157         run('rm releases/previous; mv releases/current releases/previous')
158         run('ln -s %(release)s releases/current' % env)
159
160 def migrate():
161     "Update the database"
162     print '>>> migrate'
163     require('project_name', provided_by=[staging, production])
164     with cd('%(path)s/releases/current/%(project_name)s' % env):
165         run('../../../ve/bin/python manage.py syncdb --noinput' % env, pty=True)
166         if env.use_south:
167             run('../../../ve/bin/python manage.py migrate' % env, pty=True)
168
169 def collectstatic():
170     """Collect static files"""
171     print '>>> collectstatic'
172     require('project_name', provided_by=[staging, production])
173     with cd('%(path)s/releases/current/%(project_name)s' % env):
174         run('../../../ve/bin/python manage.py collectstatic --noinput' % env, pty=True)
175
176 def restart_gunicorn_debian():
177     """Restarts gunicorn server using debian script."""
178     print '>>> restart gunicorn'
179     require('project_name', provided_by=[staging, production])
180     with path('/sbin'):
181         sudo('gunicorn-debian restart %(project_name)s' % env, shell=False)
182
183 def restart_webserver():
184     """Restarts the web server."""
185     if hasattr(env, 'restart_webserver'):
186         env.restart_webserver()
187     else:
188         require('project_name', provided_by=[staging, production])
189         print '>>> restart webserver'
190         run('touch %(path)s/%(project_name)s.wsgi' % env)
191
192 def restart_celery():
193     """Restarts the Celery task queue manager."""
194     print '>>> restart Celery'
195     require('project_name', provided_by=[staging, production])
196     sudo('supervisorctl restart celery.%(project_name)s:' % env, shell=False)