Post Receive Hooks

Posted on 2020-10-12

Aim: Write a simple flask app that can be hosted on the pi such that a new commit pushed from the laptop sees the pi update the site.

Context: I want to play with microcontrollers. However, I work from a laptop and tend to move rooms quite frequently. I don’t want cables dangling around.

It’s feasible to develop over ssh, but it’s suboptimal. What would be better is:

Write on a laptop -> send to a pi and build -> flash to microcontroller

(I don’t think I care how long the builds take. Maybe I will?)

Thought this was a good first step towards this goal + I get a local server.

Setting up the remote (pi)

On the pi (pipipipi - cos I was feeling particularly inspired when naming it)

git init --bare ~/sandpit/fast-deploy.git

On the laptop

git remote add deploy ssh://pipipipi/~/sandpit/fast-deploy.git

Post receive hook

This is the hook executed when a push is received. It needs to check if the push is on the “deploy” branch, and if so execute some script.

Hooks are not tracked. So adding a hook on the laptop (ie writing the content to .git/hooks/post-receive and setting the mode chmod +755 .git/hooks/post-receive) and git pushing to pi, won’t see this hook reaching the pi.

Given that this is a key part that I wish to iterate on, I really want this automated.

One work around is to symlink the post receive hook to a tracked file. However, the remote is a bare repository: the files are not transparently available. Setting up a non-bare repository would have the problem that the pushed branch will be checked out on the remote, and git will complain.

A first source

#!/bin/bash
TARGET="/home/pi/sandpit/fast-deploy"
GIT_DIR="/home/webuser/fast-deploy.git"
BRANCH="deploy"

while read oldrev newrev ref
do
	# only checking out the master (or whatever branch you would like to deploy)
	if [ "$ref" = "refs/heads/$BRANCH" ];
	then
		echo "Ref $ref received. Deploying ${BRANCH} branch to production..."
		git --work-tree=$TARGET --git-dir=$GIT_DIR checkout -f $BRANCH
	else
		echo "Ref $ref received. Doing nothing: only the ${BRANCH} branch may be deployed on this server."
	fi
done

Someone else had a similar question. Putting these two together, let’s try the following.

Set the post-receive hook on the remote to

#!/bin/bash
GIT_WORK_TREE=~/sandpit/fast-deploy.git
export GIT_WORK_TREE
read oldrev newrev ref
git show deploy:post-receive.sh | bash -s -- $ref

(with mode set to executable) and put a modified version of the instructions in a file post-receive.sh at the root of the project.

The post-receive script looks at the ref. If the deploy branch is updated, then it copies the content of the repo into another directory, and from here executes deploy.sh.

An nginx uwsgi flask trio

Now to deploy something we can easily see.

I took the tutorial as a starting point, but hit quite a few errors. (Surely me not the tutorial?)

Ensure packages are installed on the pi:

sudo apt update
sudo apt install python3-pip python3-dev build-essential libssl-dev libffi-dev python3-setuptools

Both my laptop and pi are running versions of python3.7 so there shouldn’t be issues here.

Set up a barebones flask+uwsgi project.

The deploy.sh script to act as the entry point. The deploy script handles:

  • activating the venv
  • installing/updating python packages
  • restarting the wsgi service

The deploy script does not attempt the initial set-up of wsgi + nginx services. It didn’t seem worth the effort automating this bit too.

uwsgi service on the pi

Adding a wsgi service for the project.

In /etc/systemd/system/fast-deploy.service, write

[Unit]
Description=uWSGI instance to serve myproject
After=network.target

[Service]
User=pi
Group=pi
WorkingDirectory=/home/pi/sandpit/fast-deploy
Environment="PATH=/home/pi/sandpit/fast-deploy/venv/bin"
ExecStart=/home/pi/sandpit/fast-deploy/venv/bin/uwsgi --ini wsgi.ini

[Install]
WantedBy=multi-user.target

To start the service and ensure it restarts on subsequent boots:

sudo systemctl start fast-deploy.service
sudo systemctl enable fast-deploy.service

nginx service on the pi

Install nginx. I think this is launched with

sudo /etc/init.d/nginx start

(But I’d done this previously.)

The config files are located in /etc/nginx. From here, add the config file for the particular project in /etc/nginx/sites-available/flask_demo.conf.

server {
    listen      80;
    server_name 10.100.74.22;
    charset     utf-8;

    location / {
        include uwsgi_params;
        uwsgi_pass 127.0.0.1:3031;
    }
}

Note 10.100.74.22 is the server name of the pi.

A symlink is added then to enable the available site:

sudo ln -s /etc/nginx/sites-available/flask_demo.conf /etc/nginx/sites-enabled/

Next steps

  • kiwipi build for accelerometer.
  • pipipipi build for microcontroller
  • add pre-push tests and update requirements