fmartingr.com-legacy/fabfile.py

304 lines
7.6 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import with_statement, print_function
from functools import wraps
from os.path import dirname, abspath
import sys
from fabric.api import *
from fabric.context_managers import settings, hide
from fabric.decorators import with_settings
from fabric.contrib.files import exists
from fabric.colors import yellow, red, white, green, blue # blue is used
#
# GLOBALS
#
this_module = sys.modules[__name__]
env.LOCAL_PATH = dirname(abspath(__file__))
env.hosts = ['localhost', 'tristram.fmartingr.com']
repository = 'https://gitlab.com/fmartingr/fmartingr.com.git'
deploy_to = '/var/www/fmartingr.com'
deploy_user = 'fmartingrcom'
app_name = 'fmartingrcom'
app_configfile = '{}/shared/config/config.toml'.format(deploy_to)
django_settings = 'fmartingrcom.settings.configfile'
managepy_affix = '--settings={}'.format(django_settings)
# Doctor checkups
DOCTOR = {
'apps': ['virtualenv', 'python', 'npm', 'grunt']
}
#
# CONTEXT MANAGERS
#
def virtualenv():
"""
Activates virtualenv first
"""
return prefix('source {}/.virtualenv/bin/activate'.format(env.LOCAL_PATH))
#
# CUSTOM DECORATORS
#
def as_user(user):
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
with settings(user=user):
return func(*args, **kwargs)
return wrapper
return decorator
#
# OUTPUT
#
def header(string, color='yellow'):
color_cmd = getattr(this_module, color)
print('{} {}'.format(color_cmd('----->'), string))
def subheader(string, color='green'):
color_cmd = getattr(this_module, color)
print(' {} {}'.format(color_cmd('-'), string))
def subtext(string, color='white'):
color_cmd = getattr(this_module, color)
print(' {}'.format(color_cmd(string)))
#
# TASKS
#
@task
def setup_environment():
"""
Prepares environment for the application
"""
with cd(env.LOCAL_PATH):
execute(setup_virtualenv)
execute(setup_tools)
execute(setup_database)
@task
def setup_virtualenv(appenv='local'):
"""
Creates or updates a virtualenv
"""
if not exists('.virtualenv'):
print(yellow('Create virtualenv'))
run('virtualenv .virtualenv')
with virtualenv():
print(yellow('Installing requirements'))
run('pip install -r requirements-{}.txt'.format(appenv))
@task
def setup_tools():
# Setup frontend tools
print(yellow('Installing npm dependencies'))
run('npm install')
print(yellow('Installing bower dependencies'))
run('bower install')
@task
def setup_database():
"""
Create or update the database
"""
with virtualenv():
print(yellow('SyncDB'))
run('python manage.py syncdb')
print(yellow('Migrate'))
run('python manage.py migrate')
@task
def doctor():
print(yellow('Checking for software:'))
for app in DOCTOR['apps']:
print(white('{}'.format(app)), end=': ')
check = run('which {}'.format(app), quiet=True)
if check.succeeded:
print(green('present'))
else:
print(red('not present'))
#
# LOCAL ONLY
#
@task
@hosts(['localhost'])
def runserver():
"""
Executes local development server
"""
with cd(env.LOCAL_PATH):
with virtualenv():
run('python manage.py runserver 0.0.0.0:8000')
@task
@hosts(['localhost'])
def rungrunt():
"""
Executes grunt
"""
with cd(env.LOCAL_PATH):
run('grunt watch')
#
# DEPLOY
#
# @contextmanager
# def allow_rollback():
# try:
# yield
# except SystemExit:
# rollback()
# abort("Fail!")
@task
@hosts(['tristram.fmartingr.com'])
@as_user(deploy_user)
@with_settings(hide('warnings', 'running', 'stdout'))
def deploy():
last_release = int(run('cat {}/releases/_status'.format(deploy_to)))
current_release = last_release + 1
release_dir = '{}/releases/{}'.format(deploy_to, current_release)
header('Preparing for release v{}'.format(current_release))
# If release dir exists the last build failed
with settings(warn_only=True):
if not run("test -d {}".format(release_dir)).failed:
# Remove the build
subheader('Last build seems to have failed', 'blue')
run("rm -rf {}".format(release_dir))
subheader('Last build directory removed')
run('mkdir {}'.format(release_dir))
code_dir = '{}/code'.format(deploy_to)
header('Updating source code from cvs')
with settings(warn_only=True):
if run("test -d {}".format(code_dir)).failed:
cmd = "git clone {} {}".format(repository, code_dir)
subheader(cmd)
run(cmd)
with cd(code_dir):
subheader("git pull")
run("git pull")
header('Copying code to release')
cmd = 'cp -R {}/code {}/code'.format(deploy_to, release_dir)
subheader(cmd)
run(cmd)
header('Setting up release environment')
with cd(release_dir):
cmd = 'virtualenv virtualenv'
subheader(cmd)
run(cmd)
with prefix('source {}/virtualenv/bin/activate'.format(release_dir)):
cmd = 'pip install -r code/requirements-prod.txt'
subheader(cmd)
run(cmd)
header('Linking shared files')
cmd = 'ln -s {}/shared {}/shared'.format(deploy_to, release_dir)
subheader(cmd)
run(cmd)
header('Prepare database')
with cd(release_dir):
with prefix('source {}/virtualenv/bin/activate'.format(release_dir)):
with prefix('export APP_CONFIGFILE="{}"'.format(app_configfile)):
cmd = 'python code/manage.py migrate {} --no-input'\
.format(managepy_affix)
subheader(cmd)
run(cmd)
header('Download and move staticfiles')
with cd('{}/code'.format(release_dir)):
cmd = 'bower install'
subheader(cmd)
run(cmd)
with prefix('source {}/virtualenv/bin/activate'.format(release_dir)):
with prefix('export APP_CONFIGFILE="{}"'.format(app_configfile)):
cmd = 'python manage.py collectstatic --c --noinput {}'\
.format(managepy_affix)
subheader(cmd)
run(cmd)
# Symlink vX to current
header('Activate current release')
cmd = 'ln -sfn {}/ {}/current'.format(release_dir, deploy_to)
subheader(cmd)
run(cmd)
# Restart supervisorctl
header('Restart')
cmd = 'sudo supervisorctl restart {}'.format(app_name)
subheader(cmd)
run(cmd)
# Check if supervisor started ok!
header('Checking app restart status')
cmd = 'sleep 1'
run(cmd)
subheader(cmd, color='blue')
cmd = 'sudo supervisorctl status fmartingrcom'
subheader(cmd)
status = run(cmd)
if 'RUNNING' in status:
subheader('App seems to be running.')
else:
subheader('App may not be running!', color='red')
subtext(' '.join(status.split()), color='red')
subtext(run('tail ./shared/log/gunicorn_supervisor.log'), color='red')
# Increment version number
run('echo {} > {}/releases/_status'.format(current_release, deploy_to))
header(green('Deploy v{} finished!'.format(current_release)))
@task
@hosts(['tristram.fmartingr.com'])
@as_user(deploy_user)
@with_settings(hide('warnings', 'running', 'stdout'))
def restart():
header('Restart')
cmd = 'sudo supervisorctl restart {}'.format(app_name)
subheader(cmd)
run(cmd)
header('Checking app restart status')
cmd = 'sleep 1'
run(cmd)
subheader(cmd, color='blue')
cmd = 'sudo supervisorctl status fmartingrcom'
subheader(cmd)
status = run(cmd)
subtext(' '.join(status.split()), color='blue')