304 lines
7.6 KiB
Python
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')
|