Last active
May 23, 2022 16:00
-
-
Save rudyryk/872bafba9c1d20348d60b47331753fb9 to your computer and use it in GitHub Desktop.
Git post-receive hook sample for basic Django project
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
#!/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