Skip to content

Instantly share code, notes, and snippets.

@benzkji
Last active August 24, 2017 07:50
Show Gist options
  • Save benzkji/cb889e5a54c749452f01 to your computer and use it in GitHub Desktop.
Save benzkji/cb889e5a54c749452f01 to your computer and use it in GitHub Desktop.
my current fabfile for django deployement on djangoeurope.com (nginx / gunicorn)
from fabric.api import task, env, run, local, roles, cd, execute, hide, puts,\
sudo
import posixpath
import re
env.forward_agent = True
env.project_name = 'parkhotel' # var
env.repository = 'git@bitbucket.org:benzkji/bnzk_{project_name}.git'.format(**env)
env.local_branch = 'master'
env.remote_ref = 'origin/master'
env.requirements_files = ['requirements/deploy.pip.txt', 'requirements/basics.pip.txt', ]
env.requirements_file = env.requirements_files[0]
#==============================================================================
# Tasks which set up deployment environments
#==============================================================================
@task
def live():
"""
Use the live deployment environment.
"""
env.env_prefix = 'live'
server = '{project_name}.ch'.format(**env)
env.roledefs = {
'web': [server],
'db': [server],
}
env.system_users = {server: 'www-data'}
env.virtualenv_dir = '/srv/www/{project_name}'.format(**env)
env.project_dir = '{virtualenv_dir}/src/{project_name}'.format(**env)
env.project_conf = '{project_name}.settings.local'.format(**env)
env.restart_command = '~/init/{project_name} restart'.format(**env)
@task
def stage():
"""
Use the sandbox deployment environment on xy.bnzk.ch.
"""
env.env_prefix = 'stage'
server = 'bnzk@{project_name}.stage.bnzk.ch'.format(**env)
env.roledefs = {
'web': [server],
'db': [server],
}
env.main_user = 'bnzk'
env.system_users = {server: env.main_user} # not used yet!
env.virtualenv_dir = '/home/{main_user}/.virtualenvs/{project_name}'.format(**env)
env.project_dir = '/home/{main_user}/sites/{project_name}'.format(**env)
env.project_conf = 'project.settings._sandbox'.format(**env)
env.restart_command = '~/init/{project_name}.{env_prefix}.sh restart && ~/init/nginx restart'.format(**env)
# Set the default environment.
stage()
#==============================================================================
# Actual tasks
#==============================================================================
@task
@roles('web', 'db')
def bootstrap(action=''):
"""
Bootstrap the environment.
"""
with hide('running', 'stdout'):
exists = run('if [ -d "{virtualenv_dir}" ]; then echo 1; fi'\
.format(**env))
if exists and not action == 'force':
puts('Assuming {host} has already been bootstrapped since '
'{virtualenv_dir} exists.'.format(**env))
return
run('virtualenv {virtualenv_dir} --no-site-packages'.format(**env))
if not exists:
# why? sudo('mkdir -p {0}'.format(posixpath.dirname(env.virtualenv_dir)))
run('git clone {repository} {project_dir}'.format(**env))
# maybe!? sudo('{virtualenv_dir}/bin/pip install -e {project_dir}'.format(**env))
# nope. with cd(env.virtualenv_dir):
# sudo('chown -R {user} .'.format(**env))
# fix_permissions()
requirements()
puts('Bootstrapped {host} - database creation needs to be done manually.'\
.format(**env))
@task
def deploy(verbosity='noisy'):
"""
Full server deploy.
Updates the repository (server-side), synchronizes the database, collects
static files and then restarts the web service.
"""
if verbosity == 'noisy':
hide_args = []
else:
hide_args = ['running', 'stdout']
with hide(*hide_args):
puts('Updating repository...')
execute(update)
puts('Collecting static files...')
execute(collectstatic)
puts('Synchronizing database...')
execute(syncdb)
puts('Restarting web server...')
execute(restart)
@task
@roles('web', 'db')
def update(action='check'):
"""
Update the repository (server-side).
By default, if the requirements file changed in the repository then the
requirements will be updated. Use ``action='force'`` to force
updating requirements. Anything else other than ``'check'`` will avoid
updating requirements at all.
"""
with cd(env.project_dir):
remote, dest_branch = env.remote_ref.split('/', 1)
run('git fetch {remote}'.format(remote=remote,
dest_branch=dest_branch, **env))
with hide('running', 'stdout'):
changed_files = run('git diff-index --cached --name-only '
'{remote_ref}'.format(**env)).splitlines()
if not changed_files and action != 'force':
# No changes, we can exit now.
return
reqs_changed = False
if action == 'check':
for file in env.requirements_files:
if file in changed_files:
reqs_changed = True
break
run('git merge {remote_ref}'.format(**env))
run('find -name "*.pyc" -delete')
run('git clean -df')
#fix_permissions()
if action == 'force' or reqs_changed:
# Not using execute() because we don't want to run multiple times for
# each role (since this task gets run per role).
requirements()
@task
@roles('web')
def collectstatic():
"""
Collect static files from apps and other locations in a single location.
"""
dj('collectstatic --link --noinput')
#with cd('{virtualenv_dir}/var/static'.format(**env)):
# fix_permissions()
@task
@roles('db')
def syncdb(sync=True, migrate=True):
"""
Synchronize the database.
"""
dj('syncdb --migrate --noinput')
@task
@roles('db')
def createsuperuser():
"""
Create super user.
"""
dj('createsuperuser')
@task
@roles('web')
def restart():
"""
Copy gunicorn & nginx config, restart them.
"""
run('cp {project_dir}/project/gunicorn/{project_name}.{env_prefix}.sh $HOME/init/.'.format(**env))
run('cp {project_dir}/project/nginx/{project_name}.{env_prefix}.txt $HOME/nginx/conf/sites/.'.format(**env))
run('chmod u+x $HOME/init/{project_name}.{env_prefix}.sh'.format(**env))
run(env.restart_command)
@task
@roles('web', 'db')
def requirements():
"""
Update the requirements.
"""
run('{virtualenv_dir}/bin/pip install -r {project_dir}/{requirements_file}'\
.format(**env))
with cd('{virtualenv_dir}/src'.format(**env)):
with hide('running', 'stdout', 'stderr'):
dirs = []
for path in run('ls -db1 -- */').splitlines():
full_path = posixpath.normpath(posixpath.join(env.cwd, path))
if full_path != env.project_dir:
dirs.append(path)
if dirs:
fix_permissions(' '.join(dirs))
with cd(env.virtualenv_dir):
with hide('running', 'stdout'):
match = re.search(r'\d+\.\d+', run('bin/python --version'))
if match:
with cd('lib/python{0}/site-packages'.format(match.group())):
fix_permissions()
#==============================================================================
# Helper functions
#==============================================================================
def virtualenv(command):
"""
Run a command in the virtualenv. This prefixes the command with the source
command.
Usage:
virtualenv('pip install django')
"""
source = 'source {virtualenv_dir}/bin/activate && '.format(**env)
run(source + command)
def dj(command):
"""
Run a Django manage.py command on the server.
"""
virtualenv('{project_dir}/manage.py {dj_command} '
'--settings {project_conf}'.format(dj_command=command, **env))
#run('{virtualenv_dir}/bin/manage.py {dj_command} '
# '--settings {project_conf}'.format(dj_command=command, **env))
def fix_permissions(path='.'):
"""
Fix the file permissions. what a hack.
"""
puts("not fixing permissions yet!")
return
if ' ' in path:
full_path = '{path} (in {cwd})'.format(path=path, cwd=env.cwd)
else:
full_path = posixpath.normpath(posixpath.join(env.cwd, path))
puts('Fixing {0} permissions'.format(full_path))
with hide('running'):
system_user = env.system_users.get(env.host)
if system_user:
run('chmod -R g=rX,o= -- {0}'.format(path))
run('chgrp -R {0} -- {1}'.format(system_user, path))
else:
run('chmod -R go= -- {0}'.format(path))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment