Génération de mon CV LaTex en PDF depuis Github Actions

Génération de mon CV LaTeX en PDF depuis Github Actions

dernière mise à jour : 06/04/2023

Suite à un récent besoin de refaire mon CV, j'ai commencé à chercher mes anciens CV. Or, j'ai changé de machines entre temps; ça s'apparente donc à une fouille minutieuse (recherche dans mes boites mail, dans les fichiers sur machines 1, fichiers sur machines 2, ...) et j'ai l'impression de réinventer la roue à chaque fois.

LaTeX

Mon CV est en LaTeX.

Dans le passé, j'ai pu les faire en word, ou avec libreoffice, mais le côté texte de LaTeX en compagnie de la puissance du langage m'ont définitivement fait adopter LaTeX, au moins pour ce besoin. Le texte formatté permet un vrai versionnement sous git ou un autre gestionnaire de version.

Je sais que pour certains, quand on fait un CV, il s'agit de réussir à se vendre, mais les CV en powerpoint, très peu pour moi, je ne fais pas du marketing (non je ne ferai pas de commentaire sur les auto-évaluations avec des étoiles ou des points)...

Vous pouvez avoir une idée de comment faire avec cet article de blog.

Les mauvaises langues diront qu'on peut faire tout aussi bien avec pandoc + markdown, et ils n'ont pas vraiment tort. Néanmoins, je me suis mis à LaTeX bien avant que markdown soit à la mode et que pandoc permette cette conversion. De plus, les académiques ne jurent que par LaTeX et ce n'est pas un hasard. Donald Knuth, inventeur de TeX dont LaTeX est issu, y est presque un demi-dieu de l'informatique.

Une des forces du LaTeX est le découpage entre le contenu et l'affichage; avec le package moderncv, je peux ainsi facilement générer un autre template qui donnera un rendu totalement différent. De manière identique, pour des présentations, on utilisera beamer permettant la même chose (1).

L'environnement LaTeX à mettre en place, en revanche, peut être vite pénible, surtout si on fait appel à beaucoup de packages... Certes, il existe des outils qui permettent de faciliter ça, mais c'est coûteux en temps, dans tous les cas. Vous en avez souvent vite pour plusieurs Go de téléchargement. De mon côté, j'utilise moderncv, enumitem, csquotes, geometry, babel, hyperref, anyfontsize, inputenc, fontawesome...

Bien entendu, vous pouvez utiliser OverLeaf, mais pour un CV, j'aime bien avoir les choses en local. Overleaf a cependant un dépôt git, et rien ne vous empêche d'avoir à la fois le CV en local et sur Overleaf.

Le titre faisant mention de Github Actions, vous comprendrez que j'ai préféré cette option. Ca m'a permis de tester cette CI et de retrouver facilement ce dépôt. J'ai mis en place un dépôt Github privé(2), pour ne pas l'ouvrir au monde entier (même si ça reste Microsoft...), synchroniser mes machines, retrouver plus facilement mon CV et versionner celui-ci. Un besoin finalement assez classique, surtout pour du code, car oui, LaTeX est un langage de programmation.

Pourquoi une CI ? J'ai besoin d'intégration continue : vous l'avez compris, l'environnement LaTeX est lourd, j'ai donc besoin d'une manière de générer le PDF sans avoir besoin de tout avoir en local. Le conteneur avec tous les packages est donc une solution toute désignée.

Github Actions

Je me suis donc mis en quête de l'exploration de Github Actions, moi, l'habitué de Gitlab-CI.

Le workflow doit pouvoir générer le PDF puis pousser ces fichiers soit vers le dépôt, soit en release.

Je découvre qu'il existe déjà de nombreux workflows, même si très peu sont proposés à l'initialisation du workflow par Github (un soucis de sponsoring ?).

Côté syntaxe, il s'agit de fichiers en yaml (oui, encore), à placer dans .github/workflows dans votre dépôt.

NB : Au 1er pas, on a la nette impression que Github actions est moins puissant que Gitlab-CI, mais je n'ai pas assez creusé les 2 pour pouvoir faire une vraie comparaison digne de ce nom (mais ce n'est peut-être pas un hasard s'il y a autant de travis-ci sur github, même si travis-ci était là avant...) Voir aussi.

Premier essai

Parmi les worflows existants, Oh miracle (!), je trouve un github actions avec pdflatex ! pdflatex est un binaire permettant de générer du pdf depuis un fichier .tex. L'image utilisée dans le workflow comprend aussi lualatex ou xelatex permettant aussi de générer du PDF à partir de fichiers .tex.

Le workflow se nomme "latex-actions". La documentation propose même des exemples pour pousser ensuite les PDF vers le dépôt. Je l'adapte (je rapelle, je débute avec Github Actions), je le teste, et au bout de peu de temps, incroyable (!), je commence à avoir quelque chose qui semble fonctionner. Je dis "semble" car je me rends vite compte qu'il y a un problème...

# This is a basic workflow to help you get started with Actions

name: LaTeX Build CI

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the "main" branch
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest
    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - name: Set up Git repository
        uses: actions/checkout@v3

      - name: LaTeX Build
        uses: xu-cheng/latex-action@v2
        with:
          root_file: "*.tex"
          glob_root_file: true

      - name: Upload Full En PDF file
        uses: actions/upload-artifact@v3
        with:
          name: PDF
          path: full_ac_resume_en.pdf
      - name: Upload Std En PDF file
        uses: actions/upload-artifact@v3
        with:
          name: PDF
          path: std_resume_en.pdf
      - name: Upload Std Fr PDF file
        uses: actions/upload-artifact@v3
        with:
          name: PDF
          path: std_cv_fr.pdf

Ce workflow est facile à décomposer. On définit sur quelles actions il se déclenche, ici sur un pull request ou un push, puis un ou plusieurs jobs, découpés en étapes, steps.

On définit sur quelle image le job va tourner, à savoir ubuntu-latest. Le choix offert par Github Actions est assez limité à ce niveau; je vous invite à consulter les choix possibles ici.

Puis les étapes sont les suivantes :

  • un checkout du dépôt,
  • l'utilisation du workflow latex-actions avec (uses: xu-cheng/latex-action@v2), et sur quoi on l'applique,
  • la génération de fichiers PDF qu'on pousse vers notre dépôt (ici, en 3 étapes distinctes).

Lors de l'exécution de ce workflow, mon CV version fr est correctement généré, mais pas la version dans la langue de Shakespeare. Un comble ! Habituellement, c'est l'inverse à cause de la gestion des caractères français. Le package qui pose problème est fontawesome.

full_ac_resume_en.tex:16: Package fontawesome5 Error: Incompatible version of Font Awesome already
(fontawesome5)                loaded

Visiblement, le conteneur utilisé par ce workflow est sous Alpine Linux et il y a un soucis de compatibilité de version pour ce package...

J'ai essayé un peu de bidouiller l'image afin de rajouter les fichiers manquants, mais j'ai abandonné rapidement : Je comprends évidemment l'idée d'avoir une image la plus petite possible pour un conteneur, d'où l'utilisation de Alpine Linux. Néanmoins, l'image fait déjà plusieurs Go, la faute à l'environnement complet LaTeX. Bref, autant avoir une distribution chez qui ça fonctionne directement (c'est mon cas en local avec Fedora 37).

Je me lance donc dans la création d'un conteneur Fedora 37 comprenant LaTeX et tous mes packages. Facile, je reprends mon historique shell, en local, et l'adapte dans un Dockerfile (ne me demandez pas pourquoi la coloration syntaxique est foireuse...) :

FROM fedora:37

RUN dnf update -y && \
  dnf -y install texlive-chktex texlive-moderncv texlive-documentation \ 
                 texlive-anyfontsize texlive-geometry texlive-babel \
                 texlive-fontawesome texlive-enumitem \
                 texlive-inputenx texlive-latex-uni8 texlive-autopdf \
                 texlive-texlive-scripts texlive-mkjobtexmf texlive-metafont \
                 texlive-latex-fonts texlive-collection-fontsrecommended \
                 texlive-collection-fontsextra texlive-multirow texlive-arydshln \
                 texlive-babel-french texlive-xcolor texlive-csquotes && \
  dnf clean all

ENTRYPOINT [ "/usr/bin/pdflatex" ]

C'est du "quick & dirty". Au final, l'image du conteneur est assez volumineuse, puisqu'elle fait 2.98Go, mais elle reste inférieur à celle utilisée dans le Github Actions latex-actions, dont l'image pèse 4.27Go (à cause de l'environnement complet LaTeX) !

$ podman images                                                                
REPOSITORY                         TAG         IMAGE ID      CREATED       SIZE
localhost/remyd1/fedora-moderncv   37          90fb73ad115d  3 days ago    2.98 GB
registry.fedoraproject.org/fedora  37          38b8a6c320f3  3 weeks ago   190 MB
ghcr.io/xu-cheng/texlive-full      latest      a624d3388c1a  4 weeks ago   4.27 GB

Cette dernière faite, je la pousse sur le dockerhub (oui, il est possible de faire ça aussi avec github actions, mais je ne le savais pas encore et l'image ne devrait pas bouger (ou alors de manière exceptionnelle...)).

Version corrigée du Yaml

Voici à quoi ressemble désormais mon fichier yaml de github actions :

# This is a basic workflow to help you get started with Actions

name: LaTeX Build CI

defaults:
  run:
    shell: bash

# Controls when the workflow will run
on:
  # Triggers the workflow on push or pull request events but only for the "main" branch
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:

  build:
    runs-on: ubuntu-latest
    container: remyd1/fedora-moderncv:37
    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - name: Set up Git repository
        uses: actions/checkout@v3

      - name: LaTeX Build
        run: for texfile in $(ls *.tex); do pdflatex ${texfile}; done

      - name: Upload Full En PDF file
        uses: actions/upload-artifact@v3
        with:
          name: PDF
          path: full_ac_resume_en.pdf
      - name: Upload Std En PDF file
        uses: actions/upload-artifact@v3
        with:
          name: PDF
          path: std_resume_en.pdf
      - name: Upload Std Fr PDF file
        uses: actions/upload-artifact@v3
        with:
          name: PDF
          path: std_cv_fr.pdf

J'avais tenté de séparer ce workflow en plusieurs jobs, mais il faut alors récupérer l'environnement précédent (conteneur, espace de travail, etc), et gérer l'enchainement de ceux-ci (sinon, ils sont exécutés en parallèle). J'ai donc finalement préféré opter pour un seul gros job.

A propos de l'automatisation

Maintenant, si je voulais être encore plus feignant, serait-il possible d'automatiser encore plus tout ça... ?

La réponse est "oui", bien sûr, mais le temps que je devrais y consacrer ne sera pas rentabilisé (...).

On peut imaginer, par exemple, un code qui va lire un fichier CSV, le parser, et, en fonction du contenu, va générer le code LaTeX approprié... Oui, du code qui génère du code. C'est moche, mais ça fonctionne. On pourrait même envisager de tout coder en LaTeX..... De manière similaire, si je remplissais suffisamment ce blog, je pourrai aussi scrapper le contenu, et, en fonction des catégories, je générerai le LaTeX correspondant....

J'ai vu un code de ce type, à base de "dataframe", en R, ici (pour ceux qui ne prennent pas peur au mot "R" (avec des packages très puissants derrière (knitr, etc...))) ! Sinon, un autre code en python, à partir de notebook Jupyter.

Pour finir

En conclusion, j'espère que cet article vous permettra de gagner du temps dans la création et la mise à jour de vos CV. Pour ceux qui souhaitent utiliser le workflow ou l'image, évidemment, la licence est libre, mais je ne suis pas contre un remerciement ou une mention dans votre dépôt (pas dans le CV !) ! Je suis aussi disponible sur mastodon pour ceux qui souhaitent en discuter ou mettre à jour tout ça.

Enfin, attention à la facturation chez Github. C'est un point que je n'ai pas mentionné, mais si vous faites tourner beaucoup de jobs (pensez à utiliser max-parallel: 1 dans votre workflow) ou si vous stockez beaucoup de données, vous pourriez avoir des surprises ! Un passage à un autre gestionnaire de CI sera sûrement nécessaire si vous n'avez pas d'autre choix et/ou un budget serré.


Notes

1

A ce sujet, je préfère désormais utiliser markdown avec marp, pour lequel j'ai forké le thème dracula afin d'avoir un résultat plus sobre.

2

Ce blog statique (dont le contenu est généré par Zola depuis markdown) est également géré par un dépôt github privé connecté à Netlify.