826e7edbcaa88d77d731615082396331244131bc
[redakcja.git] / fabfile.py
1 from __future__ import with_statement # needed for python 2.5
2 from fabric.api import *
3 from fabric.contrib.files import upload_template
4
5 import os
6
7
8 # ==========
9 # = Config =
10 # ==========
11 # Globals
12 env.project_name = 'platforma'
13 env.use_south = False
14
15 # Servers
16 def staging():
17     """Use staging server"""
18     env.hosts = ['stigma.nowoczesnapolska.org.pl:2222']
19     env.user = 'zuber'
20     env.path = '/var/services/platforma'
21     env.python = '/usr/bin/python'
22     env.virtualenv = '/usr/bin/virtualenv'
23     env.pip = '/usr/bin/pip'
24     
25 def production():
26     """Use production server"""
27     env.hosts = ['wolnelektury.pl:22123']
28     env.user = 'fundacja'
29     env.path = '/opt/lektury/platforma'
30     env.python = '/opt/lektury/basevirtualenv/bin/python'
31     env.virtualenv = '/opt/lektury/basevirtualenv/bin/virtualenv'
32     env.pip = '/opt/lektury/basevirtualenv/bin/pip'
33
34
35 # =========
36 # = Tasks =
37 # =========
38 def test():
39     "Run the test suite and bail out if it fails"
40     require('hosts', 'path', provided_by=[staging, production])
41     result = run('cd %(path)s/%(project_name)s; %(python)s manage.py test' % env)
42
43 def setup():
44     """
45     Setup a fresh virtualenv as well as a few useful directories, then run
46     a full deployment. virtualenv and pip should be already installed.
47     """
48     require('hosts', 'path', provided_by=[staging, production])
49
50     run('mkdir -p %(path)s; cd %(path)s; %(virtualenv)s --no-site-packages .;' % env, pty=True)
51     run('cd %(path)s; mkdir releases; mkdir shared; mkdir packages;' % env, pty=True)
52     run('cd %(path)s/releases; ln -s . current; ln -s . previous' % env, pty=True)
53     deploy()
54
55 def deploy():
56     """
57     Deploy the latest version of the site to the servers, 
58     install any required third party modules, 
59     install the virtual host and then restart the webserver
60     """
61     require('hosts', 'path', provided_by=[staging, production])
62
63     import time
64     env.release = time.strftime('%Y-%m-%dT%H%M')
65
66     upload_tar_from_git()
67     upload_pybundle()
68     install_requirements()
69     upload_wsgi_script_sample()
70     symlink_current_release()
71     migrate()
72     restart_webserver()
73
74 def deploy_version(version):
75     "Specify a specific version to be made live"
76     require('hosts', 'path', provided_by=[localhost,webserver])
77     env.version = version
78     with cd(env.path):
79         run('rm releases/previous; mv releases/current releases/previous;', pty=True)
80         run('ln -s %(version)s releases/current' % env, pty=True)
81     restart_webserver()
82
83 def rollback():
84     """
85     Limited rollback capability. Simple loads the previously current
86     version of the code. Rolling back again will swap between the two.
87     """
88     require('hosts', provided_by=[staging, production])
89     require('path')
90     with cd(env.path):
91         run('mv releases/current releases/_previous;', pty=True)
92         run('mv releases/previous releases/current;', pty=True)
93         run('mv releases/_previous releases/previous;', pty=True)
94     restart_webserver()
95
96
97 # =====================================================================
98 # = Helpers. These are called by other functions rather than directly =
99 # =====================================================================
100 def upload_tar_from_git():
101     "Create an archive from the current Git master branch and upload it"
102     print '>>> upload tar from git'
103     require('release', provided_by=[deploy])
104     local('git archive --format=tar master | gzip > %(release)s.tar.gz' % env)
105     run('mkdir -p %(path)s/releases/%(release)s' % env, pty=True)
106     run('mkdir -p %(path)s/packages' % env, pty=True)
107     put('%(release)s.tar.gz' % env, '%(path)s/packages/' % env)
108     run('cd %(path)s/releases/%(release)s && tar zxf ../../packages/%(release)s.tar.gz' % env, pty=True)
109     local('rm %(release)s.tar.gz' % env)
110
111 def install_requirements():
112     "Install the required packages from the requirements file using pip"
113     print '>>> install requirements'
114     require('release', provided_by=[deploy])
115     run('cd %(path)s; %(pip)s install -E . requirements.pybundle' % env)
116
117 def upload_pybundle():
118     "Create pybundle with required libraries and upload it"
119     print ">>> upload pybundle"
120     require('release', provided_by=[deploy])
121     with settings(warn_only=True):
122         pip_options = run('cat %(path)s/releases/%(release)s/pip-options.txt' % env)
123         if pip_options.failed:
124             env.pip_options = ''
125         else:
126             env.pip_options = pip_options
127             
128     requirements_mtime = os.path.getmtime('requirements.txt')
129     bundle_mtime = 0
130     try:
131         bundle_mtime = os.path.getmtime('requirements.pybundle')
132     except os.error:
133         pass
134
135     if requirements_mtime > bundle_mtime:
136         local('pip bundle requirements.pybundle %(pip_options)s -r requirements.txt' % env)
137         put('requirements.pybundle', '%(path)s' % env)
138
139 def upload_wsgi_script_sample():
140     "Create and upload a wsgi script sample"
141     print ">>> upload wsgi script sample"
142     print dict(env)
143     upload_template('%(project_name)s.wsgi.template' % env, '%(path)s/%(project_name)s.wsgi.sample' % env, context=env)
144
145 def symlink_current_release():
146     "Symlink our current release"
147     print '>>> symlink current release'
148     require('release', provided_by=[deploy])
149     require('path', provided_by=[staging, production])
150     with cd(env.path):
151         run('rm releases/previous; mv releases/current releases/previous')
152         run('ln -s %(release)s releases/current' % env)
153
154 def migrate():
155     "Update the database"
156     print '>>> migrate'
157     require('project_name', provided_by=[staging, production])
158     with cd('%(path)s/releases/current/%(project_name)s' % env):
159         run('../../../bin/python manage.py syncdb --noinput' % env, pty=True)
160         if env.use_south:
161             run('../../../bin/python manage.py migrate' % env, pty=True)
162
163 def restart_webserver():
164     "Restart the web server"
165     print '>>> restart webserver'
166     run('touch %(path)s/releases/current/%(project_name)s/%(project_name)s.wsgi' % env)