# 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: danteev/texlive:latest # texlive plus inkscape and others
      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: danteev/texlive:latest # texlive plus inkscape and others
      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”

2. Here add a new token.

Click on “Add new token”

3. Configure the `read_repository` and `write_repository` permissions to be able to clone and push our files. Then click on “Create project access token”.

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

5. Now go to CI/CD settings.

6. 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. 😄