Faire une CI LaTeX avec GitLab

ci
intégration continue
git
GitLab
LaTeX
french
français
Author
Affiliation

Louis Lacoste

MIA Paris-Saclay, INRAE, AgroParisTech, Université Paris-Saclay

Published

September 23, 2025

Modified

October 6, 2025

The Final CI

Here is the content of one of my .gitlab-ci.yaml files

Important

If you see any inconsistencies, contact me by email: louis.lacoste@agroparistech.fr

variables:
  # Version de git utilisée
  GIT_VERSION: v2.30.1

  # Branche cible pour les PDF (modifiable via CI/CD variables)
  PDF_BRANCH: "pdf"

  FILE_NAMES: 2024-2025-rapport-csi 2024-2025-presentation-csi

build_tex:
  stage: build
  image: registry.gitlab.com/islandoftex/images/texlive:latest
  script:
    - |
      for FILE_NAME in $FILE_NAMES
      do
        echo "Compiling ${FILE_NAME}"
        pdflatex --shell-escape ${FILE_NAME}.tex
        if test -f ${FILE_NAME}.bcf; then
          echo "Found ${FILE_NAME}.bcf, running biber"
          biber ${FILE_NAME}
        fi
        pdflatex --shell-escape ${FILE_NAME}.tex
      done
  after_script:
    - |
      for FILE_NAME in $FILE_NAMES
      do
        echo "============================================"
        cat ${FILE_NAME}.log
        echo "============================================"
      done

  artifacts:
    paths:
      - "*.pdf"
deploy:
  stage: deploy
  image:
    name: alpine/git:${GIT_VERSION}
    entrypoint: [""]

  before_script:
    # Clone le repo dans un dossier temporaire
    - git clone "https://${GITLAB_USERNAME}:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" "${CI_COMMIT_SHA}"

    # Configure l’identité git
    - git config --global user.email "${GIT_USER_EMAIL:-$GITLAB_USER_EMAIL}"
    - git config --global user.name "${GIT_USER_NAME:-$GITLAB_USER_NAME}"

  script:
    # Déplace les PDFs compilés dans le repo cloné
    - mv *.pdf "${CI_COMMIT_SHA}/"
    - cd "${CI_COMMIT_SHA}"

    # Crée une branche orpheline (vierge, sans historique ni fichiers)
    - git checkout --orphan "${PDF_BRANCH}"
    - git reset --hard

    # Ajoute uniquement les PDF
    - git add -f *.pdf

    # Vérifie s’il y a des changements et push
    - |
      CHANGES=$(git status --porcelain | wc -l)
      if [ "$CHANGES" -gt "0" ]; then
        git commit -m "${COMMIT_MESSAGE:-Updating PDF files}"
        git push --force origin "${PDF_BRANCH}" -o ci.skip
      else
        echo "No PDF changes to commit"
      fi

Let’s break down what happens!

Variables

The variables section below is used to define variables that we will reference later.

variables:
  # Version de git utilisée
  GIT_VERSION: v2.30.1

  # Branche cible pour les PDF (modifiable via CI/CD variables)
  PDF_BRANCH: "pdf"

  FILE_NAMES: 2024-2025-rapport-csi 2024-2025-presentation-csi
  • GIT_VERSION: specifies the git version to use for the Docker image we’re going to fetch.
  • PDF_BRANCH: indicates the name of the branch on which our PDFs will be published.
  • FILE_NAMES: declares the list of file names (without extensions). In yaml format, these are strings without quotes separated by spaces

The Steps

The compilation phase build_tex

Let’s detail the build_tex step:

build_tex:
  stage: build
  image: registry.gitlab.com/islandoftex/images/texlive:latest
  script:
    - |
      for FILE_NAME in $FILE_NAMES
      do
        echo "Compiling ${FILE_NAME}"
        pdflatex --shell-escape ${FILE_NAME}.tex
        if test -f ${FILE_NAME}.bcf; then
          echo "Found ${FILE_NAME}.bcf, running biber"
          biber ${FILE_NAME}
        fi
        pdflatex --shell-escape ${FILE_NAME}.tex
      done
  after_script:
    - |
      for FILE_NAME in $FILE_NAMES
      do
        echo "============================================"
        cat ${FILE_NAME}.log
        echo "============================================"
      done

  artifacts:
    paths:
      - "*.pdf"

First, we declare stage: build to qualify the step we’re performing here. There are 3 possible stages: build, test, deploy (GitLab documentation). Here we choose build since this is the compilation of our project.

Next, we load a docker image that contains the texlive tools.

Finally, the script directive defines in bash the sequence of steps we perform to compile the project.

Conditional execution of biber

Note that we haven’t included extensions in FILE_NAMES so that we can detect here the bcf files characteristic of the bibliography.

Finally, we use the after_script directive to display the compilation log files in the CI logs.

Lastly, artifacts specifies that the artifacts we want to keep from the CI are all PDFs at the root of the repository

The deployment phase deploy

deploy:
  stage: deploy
  image:
    name: alpine/git:${GIT_VERSION}
    entrypoint: [""]

  before_script:
    # Clone le repo dans un dossier temporaire
    - git clone "https://${GITLAB_USERNAME}:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" "${CI_COMMIT_SHA}"

    # Configure l’identité git
    - git config --global user.email "${GIT_USER_EMAIL:-$GITLAB_USER_EMAIL}"
    - git config --global user.name "${GIT_USER_NAME:-$GITLAB_USER_NAME}"

  script:
    # Déplace les PDFs compilés dans le repo cloné
    - mv *.pdf "${CI_COMMIT_SHA}/"
    - cd "${CI_COMMIT_SHA}"

    # Crée une branche orpheline (vierge, sans historique ni fichiers)
    - git checkout --orphan "${PDF_BRANCH}"
    - git reset --hard

    # Ajoute uniquement les PDF
    - git add -f *.pdf

    # Vérifie s’il y a des changements et push
    - |
      CHANGES=$(git status --porcelain | wc -l)
      if [ "$CHANGES" -gt "0" ]; then
        git commit -m "${COMMIT_MESSAGE:-Updating PDF files}"
        git push --force origin "${PDF_BRANCH}" -o ci.skip
      else
        echo "No PDF changes to commit"
      fi

Finally, we deploy our PDFs. For this, we load a lightweight Alpine Linux image with the Git version selected in the variables.

With the before_script directive, we clone the repository.

To create the GitLab token

Note in the git clone that we use a GITLAB_TOKEN variable, which must be created beforehand and declared in the repository.

To do this:

  1. Go to your repository settings.

In the left menus, expand “Settings” and go to “Access tokens”
  1. Here add a new token.

Click on “Add new token”
  1. Configure the read_repository and write_repository permissions to be able to clone and push our files. Then click on “Create project access token”.

  1. Your token is now displayed, copy it as it will not be displayed again.

  2. Now go to CI/CD settings.

  1. Now create the variable by clicking on “Add variable”, name it GITLAB_TOKEN, in “value” add the copied token.

The rest of the script moves the PDFs to the cloned repository, creates the publication branch and adds the pdfs.

You should now have a CI for compiling and publishing PDFs! Now you can reference your PDFs in your README.md by entering a link like:

https://mygitlab.com/myusername/myrepo/-/raw/mypdf.pdf

which allows you to display the compilation output directly in the browser. 😄