option to skip syncdb
[fnpdeploy.git] / fnpdeploy / __init__.py
index 058accf..4a47ab6 100644 (file)
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 """
 Generic fabric deployment script.
 Create a fabfile.py in the project and start it with:
@@ -21,8 +22,7 @@ Then set up some env properties:
 """
 from subprocess import check_output
 from os.path import abspath, dirname, exists, join
-from django.utils.crypto import get_random_string
-from fabric.api import *
+from fabric.api import env, task, require, run, cd, abort, sudo, path, local, put
 from fabric.context_managers import settings
 from fabric.contrib import files
 from fabric.tasks import Task, execute
@@ -31,6 +31,14 @@ env.virtualenv = '/usr/bin/virtualenv'
 env.services = None
 
 
+def get_random_string(length=12,
+                      allowed_chars='abcdefghijklmnopqrstuvwxyz'
+                                    'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'):
+    from random import SystemRandom
+    random = SystemRandom()
+    return ''.join(random.choice(allowed_chars) for i in range(length))
+
+
 @task
 def setup():
     """
@@ -86,6 +94,7 @@ def deploy():
     collectstatic()
     restart()
 
+
 @task
 def rollback():
     """
@@ -102,6 +111,7 @@ def rollback():
     collectstatic()
     restart()
 
+
 @task
 def deploy_version(version):
     """
@@ -118,6 +128,7 @@ def deploy_version(version):
     collectstatic()
     restart()
 
+
 @task
 def restart():
     for service in env.services or ():
@@ -132,6 +143,10 @@ class Service(Task):
     def upload_sample(self):
         pass
 
+    def run(self):
+        raise NotImplementedError
+
+
 class DebianGunicorn(Service):
     def __init__(self, name):
         super(Task, self).__init__()
@@ -145,12 +160,14 @@ class DebianGunicorn(Service):
         with settings(full_django_root=get_django_root_path('current')):
             upload_sample('gunicorn')
 
+
 class Apache(Service):
     def run(self):
         print '>>> restart webserver by touching WSGI'
         with path('/sbin'):
             run('touch %(app_path)s/%(project_name)s/wsgi.py' % env)
 
+
 class Supervisord(Service):
     def __init__(self, name):
         super(Task, self).__init__()
@@ -160,8 +177,10 @@ class Supervisord(Service):
         print '>>> supervisord: restart %s' % self.name
         sudo('supervisorctl restart %s' % self.name, shell=False)
 
+
 class Command(Task):
     def __init__(self, commands, working_dir):
+        super(Task, self).__init__()
         if not hasattr(commands, '__iter__'):
             commands = [commands]
         self.name = 'Command: %s @ %s' % (commands, working_dir)
@@ -174,12 +193,14 @@ class Command(Task):
             for command in self.commands:
                 run(command)
 
+
 def upload_samples():
     upload_localsettings_sample()
     upload_nginx_sample()
     for service in env.services:
         service.upload_sample()
 
+
 def upload_sample(name, where="samples/", ext='.sample', **kwargs):
     require('app_path', 'project_name')
     upload_path = '%s/%s%s%s' % (env['app_path'], where, name, ext)
@@ -191,15 +212,19 @@ def upload_sample(name, where="samples/", ext='.sample', **kwargs):
         template = join(dirname(abspath(__file__)), 'templates/' + name + '.template')
     files.upload_template(template, upload_path, env, **kwargs)
 
+
 def upload_localsettings_sample():
-    "Fill out localsettings template and upload as a sample."
+    """Fill out localsettings template and upload as a sample."""
     env.secret_key = get_random_string(50)
     upload_sample('localsettings.py', where="")
 
-upload_nginx_sample = lambda: upload_sample('nginx')
+
+def upload_nginx_sample():
+    upload_sample('nginx')
+
 
 def upload_tar_from_git():
-    "Create an archive from the current Git branch and upload it"
+    """Create an archive from the current Git branch and upload it"""
     print '>>> upload tar from git'
     require('release', provided_by=[deploy])
     require('app_path')
@@ -211,31 +236,39 @@ def upload_tar_from_git():
     run('cd %(app_path)s/releases/%(release)s && tar zxf ../../packages/%(release)s.tar.gz' % env, pty=True)
     local('rm %(release)s.tar.gz' % env)
 
+
 def install_requirements():
-    "Install the required packages from the requirements file using pip"
+    """Install the required packages from the requirements file using pip"""
     print '>>> install requirements'
     require('release', provided_by=[deploy])
     require('app_path')
-    if not files.exists('%(app_path)s/ve' % env):
+    set_ve()
+    if not files.exists(env.ve):
+        # HERE: maybe venv?
         require('virtualenv')
-        run('%(virtualenv)s %(app_path)s/ve' % env, pty=True)
+        run('%(virtualenv)s %(ve)s' % env, pty=True)
     with cd('%(app_path)s/releases/%(release)s' % env):
-        run('%(app_path)s/ve/bin/pip install -r requirements.txt' % env, pty=True)
+        run('%(ve)s/bin/pip install -r %(reqs)s' % {
+            've': env.ve, 'reqs': env.get('requirements_file', 'requirements.txt')}, pty=True)
     with cd(get_django_root_path(env['release'])):
         # Install DB requirement
         database_reqs = {
             'django.db.backends.postgresql_psycopg2': 'psycopg2',
             'django.db.backends.mysql': 'MySQL-python',
         }
-        databases = run('''DJANGO_SETTINGS_MODULE=%(project_name)s.settings %(app_path)s/ve/bin/python -c 'from django.conf import settings;              print " ".join(set([d["ENGINE"] for d in settings.DATABASES.values()]))' ''' % env)
+        databases = run(
+            'DJANGO_SETTINGS_MODULE=%(project_name)s.settings '
+            '%(ve)s/bin/python -c \''
+            'from django.conf import settings;'
+            'print(" ".join(set([d["ENGINE"] for d in settings.DATABASES.values()])))\'' % env)
         for database in databases.split():
             if database in database_reqs:
                 # TODO: set pip default pypi
-                run('%(app_path)s/ve/bin/pip install ' % env + database_reqs[database])
+                run('%(ve)s/bin/pip install ' % env + database_reqs[database])
 
 
 def copy_localsettings():
-    "Copy localsettings.py from root directory to release directory (if this file exists)"
+    """Copy localsettings.py from root directory to release directory (if this file exists)"""
     print ">>> copy localsettings"
     require('release', provided_by=[deploy])
     require('app_path', 'project_name')
@@ -244,8 +277,9 @@ def copy_localsettings():
         copy_to = join(get_django_root_path(env['release']), env.get('localsettings_dst_path', env['project_name']))
         run('cp %(app_path)s/localsettings.py ' % env + copy_to)
 
+
 def symlink_current_release():
-    "Symlink our current release"
+    """Symlink our current release"""
     print '>>> symlink current release'
     require('release', provided_by=[deploy])
     require('app_path')
@@ -253,21 +287,25 @@ def symlink_current_release():
         run('rm releases/previous; mv releases/current releases/previous')
         run('ln -s %(release)s releases/current' % env)
 
+
 def migrate():
-    "Update the database"
+    """Update the database"""
     print '>>> migrate'
     require('app_path', 'project_name')
     with cd(get_django_root_path('current')):
-        run('%(app_path)s/ve/bin/python manage.py syncdb --noinput' % env, pty=True)
+        if env.get('syncdb', True):
+            run('%(ve)s/bin/python manage.py syncdb --noinput' % env, pty=True)
         for app, migration in env.get('migrate_fake', ()):
-            run('%s/ve/bin/python manage.py migrate %s --fake %s' % (env.app_path, app, migration), pty=True)
-        run('%(app_path)s/ve/bin/python manage.py migrate' % env, pty=True)
+            run('%s/bin/python manage.py migrate %s --fake %s' % (env.ve, app, migration), pty=True)
+        run('%(ve)s/bin/python manage.py migrate' % env, pty=True)
+
 
 def pre_collectstatic():
     print '>>> pre_collectstatic'
     for task in env.get('pre_collectstatic', []):
         execute(task)
 
+
 def collectstatic():
     """Collect static files"""
     print '>>> collectstatic'
@@ -276,12 +314,17 @@ def collectstatic():
         return
     require('app_path', 'project_name')
     with cd(get_django_root_path('current')):
-        run('%(app_path)s/ve/bin/python manage.py collectstatic --noinput' % env, pty=True)
+        run('%(ve)s/bin/python manage.py collectstatic --noinput' % env, pty=True)
 
 
 def get_django_root_path(release):
     require('app_path')
-    path = '%(app_path)s/releases/%(release)s' % dict(app_path = env['app_path'], release = release)
+    path = '%(app_path)s/releases/%(release)s' % dict(app_path=env['app_path'], release=release)
     if 'django_root_path' in env:
         path = join(path, env['django_root_path'])
     return path
+
+
+def set_ve():
+    if 've' not in env:
+        env['ve'] = '%s/ve' % env.app_path