Skip to content

Instantly share code, notes, and snippets.

@rudyryk
Last active May 23, 2022 16:00
Show Gist options
  • Save rudyryk/872bafba9c1d20348d60b47331753fb9 to your computer and use it in GitHub Desktop.
Save rudyryk/872bafba9c1d20348d60b47331753fb9 to your computer and use it in GitHub Desktop.
Git post-receive hook sample for basic Django project
#!/usr/bin/env python
#
# No Rights Reserved
# http://creativecommons.org/publicdomain/zero/1.0/
"""Git post-receive hook script
Suggested project structure::
/home/$_USER/
env/
$_PROJECT/
{ venv named after project }
bin/
activate
python
...
remote/
{ git bare repo }
work/
$_PROJECT/
{ project code }
www/
{ served as static content }
settings.env
settings.sample
setup.py
manage.py
var/
log/
{ dir for logs }
www -> /home/$_USER/work/$_PROJECT/www
Note: '->' means symbolic link, created via `ln -s`
Database server is PostgreSQL and the database is in UTF-8 encoding
and named after $_PROJECT.
"""
import os
HOME = os.path.expanduser('~')
PYTHON = HOME + '/env/$_PROJECT/bin/python'
GIT_BRANCH = 'master'
WORK_DIR = HOME + '/work'
VAR_DIR = HOME + '/var'
DATABASE = '$_PROJECT'
RESTART_COMMAND = 'pkill -f /$_PROJECT/bin/waitress-serve' # For Waitress
# RESTART_COMMAND = 'pkill -f /$_PROJECT/bin/uwsgi' # For uWSGI
def hook(from_commit, to_commit, branch):
"""Handle post-receive hook.
"""
git_require_branch(branch, GIT_BRANCH)
db_create(DATABASE)
makedirs(WORK_DIR)
makedirs(VAR_DIR + '/log')
git_checkout(WORK_DIR, branch)
with cd(VAR_DIR):
linkdirs([WORK_DIR + '/$_PROJECT/www', '.'])
with cd(WORK_DIR):
run(PYTHON + ' setup.py develop')
run(PYTHON + ' manage.py migrate')
run(PYTHON + ' manage.py collectstatic --noinput')
run(PYTHON + ' manage.py compilemessages')
run(RESTART_COMMAND)
###########
# Helpers #
###########
import sys
import logging
from subprocess import call
from contextlib import contextmanager
def makedirs(path, mode=0o755):
try:
os.makedirs(path, mode=mode)
echo("CREATE PATH: %s" % path)
except OSError:
pass
def linkdirs(*links):
"""Add symlinks, `links` is a list of 2-tuples (from, to).
"""
for l in links:
path, to = l[0], l[1]
makedirs(to)
run('ln -s %s %s' % (path, to), ignore_fail=True)
def venv(path):
"""Create Python env via pyvenv-3.5 command line tool.
"""
makedirs(os.path.dirname(path))
if not os.path.exists(path):
run('pyvenv-3.5 %s' % path)
def run(cmd, ignore_fail=False):
"""Run shell command and check for result. If not 0 then exit.
"""
if not cmd:
return 0
echo("RUN: %s" % cmd)
result = call(cmd, shell=True)
if result > 0:
if ignore_fail:
echo("IGNORE: exit code %s" % result)
else:
echo("ERROR: exit code %s" % result)
sys.exit(result)
return result
@contextmanager
def cd(path):
"""Change current working directory and restore on exit, context manager.
Usage:
with cd(/my/path):
run('my_command')
"""
old_path = os.getcwd()
os.chdir(path)
yield
os.chdir(old_path)
def git_checkout(to_path, branch='master'):
"""Perform git checkout call.
"""
run('GIT_WORK_TREE="%s" git checkout -f %s' % (to_path, branch))
def git_require_branch(branch, check_branch):
"""Check branch name or exit.
"""
if not branch.endswith(check_branch):
echo("Received branch %s, not matching %s." %
(branch, check_branch))
sys.exit(1)
def db_create(db):
"""Try create database. Ignore fail.
"""
if run('psql -c ";" %s' % db, ignore_fail=True):
run('createdb -E utf-8 %s' % db)
def get_console():
"""Setup stdout logger and return function to print messages
via logging.info().
"""
console = logging.getLogger('console')
console.setLevel(logging.DEBUG)
ch = logging.StreamHandler()
console.addHandler(ch)
def echo(message, *args):
more = ' '.join(map(str, args)) if args else ''
console.info(message + more)
return echo
echo = get_console() # Use echo() instead of print()
########
# Run! #
########
if __name__ == '__main__':
# Read values from stdin: from commit, to commit, branch name
args = sys.stdin.read().split()
hook(*args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment