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': [''],
'prod': [''],
REMOTE_FOLDER = '/var/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 sdist')
name = fab.local('python --fullname', capture=True)
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 --fullname', capture=True)
with'env/bin/pip install -r requirements.txt')'env/bin/pip install {0}.tar.gz'.format(name))'env/bin/alembic upgrade head')'touch wsgi/lost-tracker.wsgi')
def clean():
name = fab.local('python --fullname', capture=True)
with'rm {0}.tar.gz'.format(name))'rm requirements.txt')
def redeploy():
name = fab.local('python --name', capture=True)
with'env/bin/pip uninstall %s' % name)
def bootstrap():
with'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')
l('[ -d closure-library ] || '
'git clone')
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('No INI file found. Please fill in the following '
print(' 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()'app.ini.dist')
for sect in cfg.sections():
for opt in cfg.options(sect):
curval = cfg.get(sect, opt)
newval = fab.prompt('{}:{} = '.format(,, bold=True)), default=curval)
cfg.set(sect, opt, newval)
cfg.write(open(ini_file, 'w'))
print('>>> New config file created in ' + ini_file))
print(clr.white('=== Kept old config file from ' + ini_file, bold=True))
print('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:
print(,'is older than'),,'Assuming it has not changed and skipping '
print(,'has changed'),'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.hide('stdout', 'running', 'stderr'):
dsn =
'./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('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()
tempfile ='mktemp --tmpdir=.')
full_cmd = ['pg_dump', '--clean', '-f', tempfile] + psql_params' '.join(full_cmd))
retrieved_files = fab.get(tempfile)'rm %s' % tempfile)
if len(retrieved_files) != 1:
fab.abort('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/')