Last active
August 24, 2017 07:50
-
-
Save benzkji/cb889e5a54c749452f01 to your computer and use it in GitHub Desktop.
my current fabfile for django deployement on djangoeurope.com (nginx / gunicorn)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 = '[email protected]: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