399f1c2fcb038281a20474deb5ebce223ebb701e
[redakcja.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 import time
7
8 # ==========
9 # = Config =
10 # ==========
11 # Globals
12 env.project_name = 'platforma'
13 env.use_south = True
14 env.giturl = "git://github.com/fnp/redakcja.git"
15
16
17 # Servers
18 def staging():
19     """Use staging server"""
20     env.hosts = ['stigma.nowoczesnapolska.org.pl:2222']
21     env.user = 'platforma'
22     env.path = '/var/services/platforma'
23     env.python = '/usr/bin/python'
24     env.virtualenv = '/usr/bin/virtualenv'
25     env.pip = '/usr/bin/pip'
26     env.gitbranch = "staging"
27     common()
28
29
30 def production():
31     """Use production server"""
32     env.hosts = ['szo.nowoczesnapolska.org.pl:2225']
33     env.user = 'librarian'
34     env.gitbranch = 'master'
35     env.sandbox = '/srv/library-in-a-box/sandbox/'
36     env.virtualenv = os.path.join(env.sandbox, 'bin', 'virtualenv')
37
38     env.pip = os.path.join(env.sandbox, 'python', 'bin', 'pip')
39     env.python = os.path.join(env.sandbox, 'python', 'bin', 'python')
40     common()
41
42
43 def common():
44     env.path = os.path.join(env.sandbox, env.project_name)
45     env.target = os.path.join(env.path, 'bin', 'python')
46
47
48 # =========
49 # = Tasks =
50 # =========
51 def test():
52     "Run the test suite and bail out if it fails"
53     require('hosts', 'path', provided_by=[staging, production])
54     result = run('cd %(path)s/%(project_name)s; %(python)s manage.py test' % env)
55
56
57 def setup():
58     """
59     Setup a fresh virtualenv as well as a few useful directories, then run
60     a full deployment. virtualenv and pip should be already installed.
61     """
62     require('hosts', 'sandbox', provided_by=[staging, production])
63
64     run("mkdir -p %(path)s; mkdir -p %(path)s/www/wsgi; mkdir -p %(path)s/www/media" % env)
65
66     # make a git mirror
67     run("""cd %(path)s;
68 git clone %(giturl)s mirror;
69 cd %(path)s/mirror;
70 git pull""" % env, pty=True)
71
72     run('%(virtualenv)s %(path)s' % env, pty=True)
73     run('cd %(path)s; rm -rf releases shared packages; mkdir -p releases; mkdir -p shared; mkdir -p packages;' % env, pty=True)
74
75     # symlink static content
76     run("""cd %(path)s/www/media;
77 ln -sf %(path)s/releases/current/%(project_name)s/static static
78 ln -sf %(path)s/lib/python2.6/site-packages/django/contrib/admin/media admin-media
79 mkdir -p dynamic
80 """ % env)
81
82     run('cd %(path)s/releases; ln -s . current; ln -s . previous' % env, pty=True)
83
84     deploy()
85
86
87 def deploy():
88     """
89     Deploy the latest version of the site to the servers,
90     install any required third party modules,
91     install the virtual host and then restart the webserver
92     """
93     require('hosts', 'sandbox', 'gitbranch', provided_by=[staging, production])
94
95     env.release = time.strftime('%Y-%m-%dT%H%M')
96
97     prepare_package_from_git()
98
99     upload_wsgi_script()
100 #    upload_vhost_sample()
101     install_requirements()
102     hardlink_localsettings()
103     symlink_current_release()
104     migrate()
105     django_compress()
106     restart_webserver()
107
108
109 def deploy_version(version):
110     "Specify a specific version to be made live"
111     require('hosts', 'path', provided_by=[localhost, webserver])
112     env.version = version
113     with cd(env.path):
114         run('rm releases/previous; mv releases/current releases/previous;', pty=True)
115         run('ln -s %(version)s releases/current' % env, pty=True)
116
117     restart_webserver()
118
119
120 def rollback():
121     """
122     Limited rollback capability. Simple loads the previously current
123     version of the code. Rolling back again will swap between the two.
124     """
125     require('hosts', provided_by=[staging, production])
126     require('path')
127     with cd(env.path):
128         run('mv releases/current releases/_previous;', pty=True)
129         run('mv releases/previous releases/current;', pty=True)
130         run('mv releases/_previous releases/previous;', pty=True)
131     restart_webserver()
132
133
134 # =====================================================================
135 # = Helpers. These are called by other functions rather than directly =
136 # =====================================================================
137 def prepare_package_from_git():
138     "Create an archive from the current Git master branch and upload it"
139     print '>>> upload tar from git'
140
141     require('release', provided_by=[deploy])
142
143     run('mkdir -p %(path)s/releases/%(release)s' % env, pty=True)
144     run('mkdir -p %(path)s/packages' % env, pty=True)
145     run('cd %(path)s/mirror; git pull; git archive --format=tar %(gitbranch)s | gzip > %(path)s/packages/%(release)s.tar.gz' % env)
146     run('cd %(path)s/releases/%(release)s && tar zxf ../../packages/%(release)s.tar.gz' % env, pty=True)
147
148
149 #def upload_vhost_sample():
150 #    "Create and upload Apache virtual host configuration sample"
151 #    print ">>> upload vhost sample"
152 #    files.upload_template('%(project_name)s.vhost.template' % env, '%(path)s/%(project_name)s.vhost.sample' % env, context=env)
153
154
155 def upload_wsgi_script():
156     "Create and upload a wsgi script sample"
157     print ">>> upload wsgi script sample"
158     files.upload_template('%(project_name)s/config/%(project_name)s.wsgi.template' % env, '%(path)s/www/wsgi/%(project_name)s.wsgi' % env, context=env)
159     run('chmod ug+x %(path)s/www/wsgi/%(project_name)s.wsgi' % env)
160
161
162 def install_requirements():
163     "Install the required packages from the requirements file using pip"
164
165     print '>>> Instaling global requirements'
166     require('release', provided_by=[deploy])
167     run('cd %(path)s; %(path)s/bin/pip install -r %(path)s/releases/%(release)s/%(project_name)s/config/requirements.txt' % env, pty=True)
168
169     print '>>> Instaling site requirements'
170
171     with settings(warn_only=True):
172         run('cd %(path)s; %(path)s/bin/pip install -r %(sandbox)s/etc/%(project_name)s/requirements.txt' % env, pty=True)
173
174
175 def hardlink_localsettings():
176     "Hardlink localsettings.py from root directory to release directory (if this file exists)"
177     print ">>> Hardlink localsettings"
178     require('release', 'path', provided_by=[deploy])
179     require('sandbox', provided_by=[staging, production])
180
181     # hardlink localsettings
182     with settings(warn_only=True):
183         run('ln %(sandbox)s/etc/%(project_name)s/localsettings.py %(path)s/releases/%(release)s/%(project_name)s' % env)
184
185
186 def symlink_current_release():
187     "Symlink our current release"
188     print '>>> symlink current release'
189     require('release', provided_by=[deploy])
190     require('path', provided_by=[staging, production])
191     with cd(env.path):
192         run('rm releases/previous; mv releases/current releases/previous')
193         run('ln -s %(release)s releases/current' % env)
194
195
196 def migrate():
197     "Update the database"
198     print '>>> migrate'
199     require('project_name', provided_by=[staging, production])
200     with cd('%(path)s/releases/current/%(project_name)s' % env):
201         run('%(target)s manage.py syncdb --noinput' % env, pty=True)
202
203         if env.use_south:
204             run('%(target)s manage.py migrate' % env, pty=True)
205
206
207 def django_compress():
208     "Update static files"
209     print '>>> migrate'
210     require('project_name', provided_by=[staging, production])
211     with cd('%(path)s/releases/current/%(project_name)s' % env):
212         run('%(target)s manage.py synccompress --force' % env, pty=True)
213
214
215 def restart_webserver():
216     "Restart the web server"
217     print '>>> restart webserver'
218     run('touch %(path)s/www/wsgi/%(project_name)s.wsgi' % env)