Simple Deployments using Git as Transport


This is largely an elaboration on my other post on using Git for deployment

I like to write small toy programs as web apps, like the Curl-to-Perl converter or my weather forecast app. My current tool of choice for writing such web apps is Mojolicious, and the local development is quite nice as it comes with a local web server built in.

XXX screenshot of the weather app

The internet

But obviously a web application is no fun if you can't put it online and use it from wherever, or show it around. While I have a server on the internet and it has a webserver, updating my software while it is in development is inconvenient. When it is inconvenient, I don't do it often, so I want to remove that inconvenience as far as possible.


Copying files to the target machine

It's easy to copy files using scp or rsync. My uplink is pretty fast nowadays so I can conveniently copy 10MB within a second.

I program in Perl, and I would need to copy the needed Perl modules as well. This fails when I need to use Perl modules with a binary component like a C library. So copying files alone will not work.

Running programs on the target machine

If I'm only copying the data that can be easily created locally, I need a way to run programs on the remote machine. This is possible for example through ssh -c.

But as most of my webapps are writen with Perl as the backend, I need to run at least cpanm --installdeps to install the modules needed. Often I also want to regenerate other files like manifest.json and/or compress assets. Usually, these other jobs are done through a Makefile.

Using Git as transport and runner

I use Git as my version control system. In Git, I usually check in almost everything of interest to a project. Instead of writing a shell script that I run locally which uploads the files and then kicks off a remote build, I (ab)use the Git post-receive hook to work as my program runner and the Git transport mechanism for transferring the data.

Git as transport

Git can download and upload changes from other git Repositories. It can use a variety of transport mechanisms:

  • file copy from/to a local directory
  • file copy via the git protocol
  • file copy via the ssh / scp protocol

The last protocol is of convenient interest to me, as it means I can simply have a Git repository on the webserver machine and git push will upload my local changes to the remote webserver.

Anatomy of the Git post-receive hook

Whenever Git receives a complete set of changes in a repository, it will then kick off the post-receive hook. The post-receive hook is a program intended to be customized by the user to perform tasks whenever the event arrives.

In my case, I use that post-receive hook to perform all tasks that I want to be done on the webserver:

  • check out the latest state of my webapp into a directory
  • install modules needed by my webapp
  • perform other tasks as specified by a Makefile


Setting up the post-receive hook is fairly simple:

  1. Create a remote directory for the repository

mkdir my-webapp.git

  1. Initialize the directory as Git repository

cd my-webapp.git && git init --bare

  1. Add the post-receive hook

Don't forget to make the file executable.

  1. Add the machine as a remote on your local repository

git remote add demo corion@that.machine.example:my-webapp.git


Deployment now looks like

git push demo

The steps of the post-receive hook

The steps performed by the hook in detail are:

Check out the latest state of my webapp into a directory

git "--work-tree=${CHECKOUT_DIR}" "--git-dir=${REPO}" checkout -f

From the Git repository, we checkout the current state into a target directory.

Install modules needed by my webapp

I like to install all modules needed by a webapp into a directory local to that webapp. This means more maintenance, but it also means that changes to one webapp don't break other webapps. For additional safety, I also reset the PERL5LIB environment variable so even if the hook is run manually it won't install or use modules outside of the app-specific directory.

PERL5LIB=${BASE}/${DIST}/lib /home/corion/perl/bin/cpanm --installdeps "${CHECKOUT_DIR}" -l "${CHECKOUT_DIR}/extlib" --notest

Run post-install steps

Some assets of the webapp might need to be (re)compressed. make is a convenient tool to update files based on the date of other files:

cd "${CHECKOUT_DIR}/public" && make deploy

The post-receive hook in its full glory


REPO=$( cd "$GIT_DIR" || exit; pwd)
BASE=$(cd "${REPO}/.." || exit; pwd)
DIST=$(basename "${CHECKOUT_DIR}")

git "--work-tree=${CHECKOUT_DIR}" "--git-dir=${REPO}" checkout -f
PERL5LIB=${BASE}/${DIST}/lib /home/corion/perl/bin/cpanm --installdeps "${CHECKOUT_DIR}"  -l "${CHECKOUT_DIR}/extlib" --notest

cd "${CHECKOUT_DIR}/public" && make deploy

See Also

Git::Hooks - a Perl program for Git hooks

git-init - create or edit your default Git hooks

Other approaches


A bundler for Perl. This requires you to have the same Perl compiler and compiler flags locally as you have on the remote machine as it compiles all artifacts locally.

move-year - A minute tool to help me


Time progresses on its own, which manifests itself in timestamps in filenames increasing. Such files are files from the tax office, bank statements and other stuff I download from such websites. I like to have them moved from the Downloads directory into directories that are in my backup run, and already have a script that reacts to such downloads being completed.

This script previously dumped the files in the root directory for documents of my bank or the tax office via

mv -i ~/Downloads/bank-statement-2022-01-01.pdf ~/Documents/finance/my-bank/
mv -i ~/Downloads/tax-report-01012022.pdf ~/Documents/finance/taxes/

So from time to time, I went through ~/Documents/finance/my-bank/ and moved the files into directories according to the year they were in.

But I realized that a small tool can do that automatically and also create the directories directly. And ideally, I don't need to tell the tool very much at all:

move-year --create -ymd -i ~/Downloads/bank-statement-2022-01-01.pdf ~/Documents/finance/my-bank/
move-year --create -dmy -i ~/Downloads/tax-report-31112021.pdf ~/Documents/finance/taxes/

This way, the files will automatically land in the directories ~/Documents/finance/my-bank/2022 and ~/Documents/finance/taxes/2021 .

The tool is not yet on CPAN, but it lives on Github

Movies I did watch


While staying at home this weekend, I caught up with some movies that I was made aware of via JWZ:

Bullet Train

Bullet Train Movie Poster

Amusing action thriller, with acceptable fight scenes. It's somewhat over the top, but that's to be expected for a movie set on a train, in Japan, where each role gets their own intro.

The movie could have been 15 to 30 minutes shorter but still is entertaining enough.

Day Shift

Bullet Train Movie Poster

Story-wise, it's standard vampire hunter fare, but it has some great callbacks to rap songs of the 90's and 2000. Even though Body Count's in the House may have been a bit too much on the nose. I guess 90's rap counts now as oldies.

Jwz lauded the stunts as "the stunt director was a Jackie Chan fan", but while I can't deny that this is true, the (stunt) director also didn't have the budget and patience of Jackie Chan to make the stunts as good as Jackie Chans fights. Obviously, this is a Netflix production, but still entertaining enough.

Getting my Dell C1765nf to work on Debian


I'm only looking to get the printer part working. I have set up the scanning part to scan to an CIFS drive on the printer itself. I used to use the fax capability, but nowadays everybody just accepts scanned PDFs via email, so that feature is not of importance.

The printer is a relabeled Xerox 6000B, but Xerox only provides 32bit i386 binaries and my system is now amd64. This doesn't bode well for a future setup.

Installing the printer driver binaries

After installing CUPS, the magic printer driver is

apt install printer-driver-foo2zjs

Configure it for the network

Location:   192.168.1.xx
Device-URI: socket://192.168.1.xx:9100
Model:      Dell C1765 Foomatic/foo2hbpl2 (recommended)

Using that printer driver, I get feedback on the toner fill and can print in color.

Configure the printer on the computer

Enter the printer settings

Set color mode from "monochrome" to "color"

I had to reset the number of pages per sheet from 9 to 1. Most likely this was a manual misclick by me first.

Print a test page

Just to be sure, print a test page. For me, the printed configuration text reads:

Media Limits: 0.12 x 0.17 to 8.14 x 11.53 inches
Job ID: Xerox-Phaser-6000B-33
Driver Version: 1.1
Description: Dell 1675nfw
Printer Location: 192.168.1.xx
Make and Model: Dell C1765 Foomatic/foo2hbpl2 (recommended)
Printer: Xerox-Phaser-6000B