I like to write letters. I do not, however, enjoy wrangling Microsoft Word to ensure the same results each time; I use LaTex, where I can be guaranteed of consistent output.

As my library of past correspondence increases, I was looking for a way to implement version control as I drafted correspondence. Git was the obvious solution, and quite straightforward to set up. As I run a GitLab server on my Synology, I was happy storing a big backup of all of the history on a RAID device elsewhere in the house. But, I realised, it wasn’t good practice to store the generated PDFs — the binary outputs — alongside their .tex files. At the same time, I had recently put together a fairly unique build server using GitLab CI for work. I was sure that I could make something that would be useful to me in this instance too.

GitLab CI

GitLab — a server for giving a git repository a nice user interface, as well as tracking ‘issues’ and ‘merges’, has bundled with it a continuous integration server, which means that building, testing and deploying code can all be automated for each commit that’s pushed to the server.

GitLab CI Runner

This is the application that needs to be set up on an OS X/Linux machine, to compile your LaTex files for you. Download here.

Configuration

There are a few files here. .gitlab-ci.yml and compile.sh both go in the root directory of your repository, as they are for telling GitLab what to run. The make files go in each folder, sitting alongside your .tex files.

.gitlab-ci.yml

stages:
  - build

build:
  stage: build
  script:
    - compile.sh
  artifacts:
    paths:
      - "*.pdf"
      - "*/*.pdf"
      - "*/*/*.pdf"
      - "*/*/*/*.pdf"

This file runs the file compile.sh on the runner computer, and stores all of the generated files that end with *.pdf

compile.sh

#!/bin/bash

directories=$(git diff --name-only @{1} HEAD | grep -e '\.tex$' | while read line; do echo "./$line"; done | sed 's/\(.*\)\/.*/\1/')

if [ -n "$directories" ]
then
        echo "Directories for compilation:"
        echo "$directories"

        for d in "$directories";
        do
                cd "$d"
                if [ -e makefile ]
                then
                        make
                else
                        pwd
                        pdflatex *.tex
                fi
        done
fi

This file searches for all newly modified *.tex files, and runs make on them.

Make files

Makefile

all:
        ./make_script.sh
clean:
        ./make_clean.sh

make_script.sh

#!/bin/bash

for file in *.tex;
do
        bn=`basename $file .tex`
        find . -type f -name "$bn*" -not -name "*.tex" -exec rm -rf {} \;
        pdflatex -shell-escape $bn
        bibtex $bn
        makeglossaries $bn
        makeindexes $bn
        pdflatex -shell-escape $bn
        makeglossaries $bn
        pdflatex -shell-escape $bn
        makeglossaries $bn
        pdflatex -shell-escape $bn
        pdflatex -shell-escape $bn
done;

make_clean.sh

#!/bin/bash

for file in *.tex;
do
        bn=`basename $file .tex`
        find . -type f -name "$bn*" -not -name "*.tex" -not -name "*.pdf" -exec rm -rf {} \;
done

So?

This is superior to most other setups for a couple of reasons.

Firstly, previous development in LaTeX assumed that your editing devices had the rather large LaTeX packages installed; if you’re on an iPad, this can be a difficult thing to manage. My solution obviates the coupling of a LaTeX compiler to a LaTeX editor.

Secondly, the PDFs are available under ‘builds’, ready to be downloaded immediately. Because the PDFs generated are now linked to a particular build, and therefore a particular commit, a complete revision is available for them, along with their associated files. However, the PDFs — binary outputs of what you’ve written — aren’t there to clutter up your git repository (nor are the associated .aux files, etc).