Fetching contributors…
Cannot retrieve contributors at this time
286 lines (234 sloc) 8.5 KB
from __future__ import print_function
from ConfigParser import SafeConfigParser
from os.path import exists
import fabric.api as fab
import fabric.colors as clr
fab.env.roledefs = {
'failover': ['lostlu@dozer.foobar.lu'],
'prod': ['lost_tracker_backup@eurinfo.net'],
REMOTE_FOLDER = '/var/www/lost.lu/www'
PLOVR_REVISION = '9f12b6c'
PLOVR = 'plovr/build/plovr-{}.jar'.format(PLOVR_REVISION)
CLOSURE_REVISION = '57bdfe0093c'
@fab.roles('prod', 'failover')
def deploy():
def upload():
fab.local('python setup.py sdist')
name = fab.local('python setup.py --fullname', capture=True)
with fab.cd(REMOTE_FOLDER):
fab.put('dist/{0}.tar.gz'.format(name), '.')
fab.put('alembic.ini', '.')
fab.put('alembic', '.')
fab.put('requirements.txt', '.')
def install():
name = fab.local('python setup.py --fullname', capture=True)
with fab.cd(REMOTE_FOLDER):
fab.run('env/bin/pip install -r requirements.txt')
fab.run('env/bin/pip install {0}.tar.gz'.format(name))
fab.run('env/bin/alembic upgrade head')
fab.run('touch wsgi/lost-tracker.wsgi')
def clean():
name = fab.local('python setup.py --fullname', capture=True)
with fab.cd(REMOTE_FOLDER):
fab.run('rm {0}.tar.gz'.format(name))
fab.run('rm requirements.txt')
def redeploy():
name = fab.local('python setup.py --name', capture=True)
with fab.cd(REMOTE_FOLDER):
fab.run('env/bin/pip uninstall %s' % name)
def bootstrap():
with fab.cd(REMOTE_FOLDER):
fab.run('mkdir -p wsgi')
fab.put('wsgi/lost-tracker.wsgi', 'wsgi')
def alembic():
"Run DB Migrations"
with fab.settings(shell_env={'PYTHONWARNINGS': ''}):
fab.local('./env/bin/alembic upgrade head')
def develop():
Sets up a new development environment. Should be run right after cloning
the repo.
l = fab.local
ini_file = '.mamerwiselen/lost-tracker/app.ini'
l('[ -d env ] || virtualenv env')
l('./env/bin/pip install "setuptools>=0.8"') # needed by IMAPClient
# some packages are unavailable on pypi :( -> Use requirements.txt
l('./env/bin/pip install -r requirements.txt')
l('./env/bin/pip install -e .[dev,test]')
l('mkdir -p __libs__')
with fab.lcd('__libs__'):
l('[ -d plovr ] || '
'git clone https://github.com/bolinfest/plovr.git')
l('[ -d closure-library ] || '
'git clone https://github.com/google/closure-library.git')
with fab.lcd('__libs__/plovr'):
l('git fetch')
l('git checkout {}'.format(PLOVR_REVISION))
with fab.lcd('__libs__/closure-library'):
l('git fetch')
l('git checkout {}'.format(CLOSURE_REVISION))
l('mkdir -p .mamerwiselen/lost-tracker')
with fab.settings(warn_only=True):
ini_is_missing = l('[ -f ' + ini_file + ' ]').failed
if ini_is_missing:
print(clr.green('No INI file found. Please fill in the following '
print(clr.green(' Look at "app.ini.dist" for documentation.'))
print(clr.yellow(' The file will be stored in {}'.format(ini_file)))
print(clr.yellow(' You can change this at any time. If you do, you '
'need to restart the application if it is still '
cfg = SafeConfigParser()
for sect in cfg.sections():
for opt in cfg.options(sect):
curval = cfg.get(sect, opt)
newval = fab.prompt('{}:{} = '.format(
clr.blue(opt, bold=True)), default=curval)
cfg.set(sect, opt, newval)
cfg.write(open(ini_file, 'w'))
print(clr.green('>>> New config file created in ' + ini_file))
print(clr.white('=== Kept old config file from ' + ini_file, bold=True))
print(clr.green('Applying database migrations...'))
print(clr.yellow('NOTE: The DB must exist, and the URL in %r must be '
'correct. I will pause now to give you a chance to fix '
'that, if needed. Press ENTER when ready.' % ini_file))
print(clr.yellow('This step can always be re-executed by running '
'"fab alembic"'))
fab.prompt('Press ENTER when ready...')
def build():
Compile JS sources.
from os import stat
from json import load
with open('plovr-config.js') as fp:
plovr_config = load(fp)
input_file = plovr_config['inputs']
output_file = plovr_config['output-file']
stat_in = stat(input_file)
if exists(output_file):
stat_out = stat(output_file)
needs_build = stat_in.st_mtime > stat_out.st_mtime
needs_build = True
if not needs_build:
clr.green('is older than'),
clr.green('Assuming it has not changed and skipping '
clr.green('has changed'),
clr.green('recompiling to'),
fab.local('java -jar __libs__/{} build plovr-config.js'.format(PLOVR))
def extract_pot():
fab.local('./env/bin/pybabel extract '
'-F babel.cfg -o messages.pot lost_tracker')
def babel_init(locale):
Initialise support for a new locale.
fab.local('./env/bin/pybabel init '
'-i messages.pot -d lost_tracker/translations -l {}'.format(
def babel_compile():
Compile all translations into the application.
fab.local('./env/bin/pybabel compile -f -d lost_tracker/translations')
def babel_update():
Extracts translations and updates the pot template.
fab.local('./env/bin/pybabel update '
'-i messages.pot '
'-d lost_tracker/translations')
def _get_psql_params():
return a list of PostgreSQL parameters. These parameters are usable for
commands like ``psql`` or ``pg_dump``. It uses a config-file containing a
``db.dsn`` option to determine those parameters.
from urlparse import urlparse
with fab.cd(REMOTE_FOLDER), fab.hide('stdout', 'running', 'stderr'):
dsn = fab.run(
'./env/bin/python -c "'
'from config_resolver import Config; '
'import logging; logging.basicConfig(level=logging.ERROR); '
"config = Config('mamerwiselen', 'lost-tracker'); "
"print(config.get('db', 'dsn'))"
if not dsn:
fab.abort(clr.red('Unable to get DSN from config file!'))
# psql does not support SQLAlchemy Style DSNs so we need to parse it and
# reconstruct a proper command
dsn_detail = urlparse(dsn)
psql_params = ['-U', dsn_detail.username]
if dsn_detail.port:
psql_params.extend(['-p', dsn_detail.port])
if dsn_detail.hostname and dsn_detail.hostname != 'localhost':
psql_params.extend(['-h', dsn_detail.hostname])
return psql_params
def pull_db():
psql_params = _get_psql_params()
with fab.cd(REMOTE_FOLDER):
tempfile = fab.run('mktemp --tmpdir=.')
full_cmd = ['pg_dump', '--clean', '-f', tempfile] + psql_params
fab.run(' '.join(full_cmd))
retrieved_files = fab.get(tempfile)
fab.run('rm %s' % tempfile)
if len(retrieved_files) != 1:
fab.abort(clr.red('Expected to retrieve exactly one file. '
'But got %d!' % len(retrieved_files)))
local_file = next(iter(retrieved_files))
fab.local('psql -X -q -f %s lost_tracker_2016' % local_file)
fab.local('rm %s' % local_file)
def serve_plovr():
Run JS development server.
fab.local('java -jar __libs__/{} serve plovr-config.js'.format(PLOVR))
def serve_web():
Run development server.
fab.local('./env/bin/python lost_tracker/main.py')