Carnet d'aventure DevOps #2 : Mini Projet - Un README de DevOpseur !

6 min de lecture

Je travaillais sur le README du repo GitHub de ce blog quand je me suis dit : Ça serait cool si j’avais un badge qui calcule automatiquement le nombre d’articles que j’ai écrits !
Et puis pourquoi pas un badge qui calcule le nombre total de mots écrits dans tous mes articles ?! 😲
Voyons ensemble comment faire ça et pourquoi c’est un peu du DevOps. 😉

Le README, c’est quoi ?

Le README est un fichier texte qui se trouve généralement à la racine d’un projet.

Il sert à fournir des informations essentielles sur le projet, telles que son objectif, son installation, son utilisation, sa contribution, etc.

Il est souvent écrit en Markdown (fichier README.md) pour permettre une mise en forme simple et lisible.

Pour être plus clair, dans un repo GitHub, le README est la première chose que les visiteurs voient lorsqu’ils accèdent au projet.

Il sert de guide pour comprendre ce qu’est le projet, comment l’utiliser et comment y contribuer.

C’est au final, la page d’accueil de votre projet.

Mais pourquoi diable automatiser le README ?

Automatiser le README peut être utile pour plusieurs raisons :

  • Mise à jour automatique : Si votre projet évolue fréquemment, automatiser certaines parties du README peut garantir que les informations restent à jour sans nécessiter de modifications manuelles constantes.
  • Cohérence : L’automatisation peut aider à maintenir une structure et un format cohérents dans le README, ce qui facilite la lecture et la compréhension pour les utilisateurs.
  • Gain de temps : En automatisant certaines tâches, vous pouvez économiser du temps et des efforts, surtout si vous avez plusieurs projets ou si vous devez souvent mettre à jour les informations.
  • Intégration avec d’autres outils : L’automatisation peut permettre d’intégrer le README avec d’autres outils ou services, comme des badges de statut, des statistiques de projet, etc.
  • Parce que c’est cool ! 😎 : Avouons-le, il y a quelque chose de satisfaisant à voir des informations se mettre à jour automatiquement.

Quoi automatiser dans le README ?

On peut automatiser plusieurs éléments dans un README, en fonction des besoins du projet. Voici quelques exemples courants :

  • Badges de statut : Afficher des badges qui indiquent l’état du projet, comme le statut de la build, la couverture des tests, les versions, etc.
  • Statistiques du projet : Afficher des statistiques telles que le nombre de contributeurs, le nombre de commits, le nombre de lignes de code, etc.
  • Instructions d’installation : Générer automatiquement des instructions d’installation basées sur les dépendances du projet.
  • Changelog : Générer automatiquement un changelog basé sur les commits ou les versions.
  • Liste des contributeurs : Afficher automatiquement une liste des contributeurs au projet.

Mais ici, pour mon mini-projet, je vais automatiser deux choses :

  1. Le nombre d’articles écrits dans ce blog.
  2. Le nombre total de mots écrits dans tous mes articles.

Comment automatiser le README ?

Pour automatiser le README, on peut utiliser plusieurs outils et techniques. Voici quelques options courantes :

  • Scripts personnalisés : Écrire des scripts en Python, JavaScript, Bash, etc., pour extraire les informations nécessaires et mettre à jour le README.
  • GitHub Actions : Utiliser des workflows GitHub Actions pour automatiser la mise à jour du README en fonction des événements GitHub, comme les push ou les pull requests.
  • Outils tiers : Utiliser des outils ou des services tiers qui offrent des fonctionnalités d’automatisation pour les README, comme Shields.io pour les badges.

Pour mon mini-projet, j’ai choisi d’utiliser GitHub Actions pour automatiser la mise à jour du README.

GitHub Actions ?

GitHub Actions est une fonctionnalité de GitHub qui permet d’automatiser des tâches en réponse à des événements dans un dépôt GitHub.

Ces tâches sont définies dans des fichiers YAML appelés workflows, qui spécifient les actions à exécuter et les conditions pour les déclencher.

GitHub Actions est souvent utilisé pour des tâches telles que l’intégration continue (CI), le déploiement continu (CD), la gestion des versions, et bien sûr, l’automatisation de la documentation comme le README.

Comment ça marche ?

GitHub Actions fonctionne en utilisant des workflows, qui sont des ensembles d’actions définies dans des fichiers YAML.

Chaque workflow est déclenché par un événement spécifique, comme un push, une pull request, ou un horaire programmé.

Un workflow est composé de plusieurs jobs, qui sont des ensembles d’étapes à exécuter.

Chaque étape peut exécuter une commande, un script, ou utiliser une action prédéfinie.

En gros, ici on va créer un workflow qui s’exécute à chaque fois qu’on push un article dans le dépôt.

Ce workflow va compter le nombre d’articles et le nombre de mots, puis mettre à jour le README avec ces informations.

Mon workflow GitHub Actions pour automatiser le README

Comment faire ça concrètement ? Tout d’abord il faut ajouter un fichier YAML dans le dossier .github/workflows/ de votre repo GitHub. Ce fichier va définir le workflow.

📝 Journal de Bot-35 | articles-badge.yml
Réduire la fenêtre
Copier
name: Articles-Badge

on:
  push: 
    branches: [ main ]
    # Ne s'éxécute que si le dossier des articles est modifié
    paths:
      - 'src/content/posts/**'
  # Lancement manuellement si besoin
  workflow_dispatch:

permissions:
  contents: write

jobs:
  build-badges:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 1   # ne clone que le dernier commit (0 si vous voulez clone l'historique)
          ref: main        # force une branche ou un tag précis
          persist-credentials: true  # garde le token pour pouvoir faire git push

      - name: Compter les articles (.md/.mdx)
        id: count
        shell: bash
        run: |
          set -euo pipefail
          POSTS_DIR="src/content/posts"

          # Compter les fichiers .md et .mdx
          total=$(find "$POSTS_DIR" -type f \( -name '*.md' -o -name '*.mdx' \) | wc -l || true)

          # Publiés = fichiers qui ne contiennent PAS "draft: true" (tolère espaces)
          published=0
          while IFS= read -r -d '' f; do
            if ! grep -Eq '^[[:space:]]*draft:[[:space:]]*true' "$f"; then
              published=$((published + 1))
            fi
          done < <(find "$POSTS_DIR" -type f \( -name '*.md' -o -name '*.mdx' \) -print0)

          echo "total=$total" >> "$GITHUB_OUTPUT"
          echo "published=$published" >> "$GITHUB_OUTPUT"

      - name: Compter les mots (approx)
        id: words
        shell: bash
        run: |
          set -euo pipefail
          POSTS_DIR="src/content/posts"

          words_total=0
          words_published=0

          while IFS= read -r -d '' f; do
            # 1) Enlève le front-matter YAML (--- ... --- en tête)
            # 2) Enlève les blocs de code ```...``` ou ~~~...~~~
            # 3) Nettoie le markdown/HTML pour ne garder que le texte
            text="$(
              awk 'NR==1 && /^---$/ {inmeta=1; next} inmeta && /^---$/ {inmeta=0; next} !inmeta {print}' "$f" \
              | awk 'BEGIN{code=0} /^```/ || /^~~~/ {code=!code; next} !code {print}' \
              | sed -E -e 's/`{1,3}[^`]*`{1,3}//g' -e 's/!\[[^]]*\]\([^)]*\)//g' -e 's/\[([^]]+)\]\([^)]*\)/\1/g' -e 's/<[^>]+>//g' -e 's/[#*_>~`{}[\]()>+=-]+/ /g'
            )"

            count=$(printf "%s" "$text" | wc -w | tr -d ' ')
            words_total=$((words_total + count))

            if ! grep -Eq '^[[:space:]]*draft:[[:space:]]*true' "$f"; then
              words_published=$((words_published + count))
            fi
          done < <(find "$POSTS_DIR" -type f \( -name '*.md' -o -name '*.mdx' \) -print0)

          {
            echo "words_total=$words_total"
            echo "words_published=$words_published"
          } >> "$GITHUB_OUTPUT"


      - name: Générer les badges SVG (style shield-like)
        shell: bash
        run: |
          set -euo pipefail

          mkbadge () {
            label="$1"; value="$2"; file="$3"
            labw=100
            valw=$(( ${#value} * 10 + 36 ))
            width=$(( labw + valw ))

            cat > "$file" <<EOF
          <svg xmlns="http://www.w3.org/2000/svg" width="$width" height="20" role="img" aria-label="$label: $value">
            <linearGradient id="s" x2="0" y2="100%">
              <stop offset="0" stop-color="#fff" stop-opacity=".7"/>
              <stop offset=".1" stop-opacity=".1"/>
              <stop offset=".9" stop-opacity=".3"/>
              <stop offset="1" stop-opacity=".5"/>
            </linearGradient>
            <mask id="m"><rect width="$width" height="20" rx="3" fill="#fff"/></mask>
            <g mask="url(#m)">
              <rect width="$labw" height="20" fill="#555"/>
              <rect x="$labw" width="$valw" height="20" fill="#4c1"/>
              <rect width="$width" height="20" fill="url(#s)"/>
            </g>
            <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
              <text x="$((labw/2))" y="14">$label</text>
              <text x="$((labw + valw/2))" y="14">$value</text>
            </g>
          </svg>
          EOF
          }

          mkbadge "Nb de posts " "${{ steps.count.outputs.published }}" "articles-badge.svg"
          mkbadge "Nb de mots " "${{ steps.words.outputs.words_published }}" "words-badge.svg"




      - name: Commit et push des badges
        shell: bash
        run: |
          set -euo pipefail
          git config user.name "github-actions"
          git config user.email "github-actions@github.com"
          git add articles-badge.svg words-badge.svg || true
          if ! git diff --cached --quiet; then
            git commit -m "chore(badge): update articles count [skip ci]"
            git push
          else
            echo "Pas de changement, rien à committer."
          fi

Explications du workflow

À la lecture du script, vous avez probablement une idée déjà assez claire de ce qu’il fait.
Mais voici tout de même une explication détaillée de chaque section (vous êtes là pour ça, faudrait pas que je l’oublie 🙈) :

  1. name : Donne un nom au workflow (qui apparaîtra dans GitHub), ici “Articles-Badge”.
  2. on : Définit les événements qui déclenchent le workflow.
    Ici, il se déclenche sur un push vers la branche main et uniquement si des fichiers dans src/content/posts/ sont modifiés.
    Il peut aussi être déclenché manuellement via l’interface GitHub grâce à workflow_dispatch.
  3. permissions : Ici, le but étant de modifier le README, on donne la permission d’écrire sur le content (content ici représente l’ensemble des données du repo et n’a pas de rapport avec le nom du dossier src/content/).
  4. jobs : Définit les actions à effectuer. Elles peuvent être parallèles ou séquentielles, ce qui est le cas ici avec l’utilisation de steps.
    • build-badges : Le nom du job. Interne à GitHub Actions, il sert par exemple à créer des dépendances de job (needs: build-badges). Pour donner un nom spécifique au job dans l’interface GitHub on utilisera name (name: Générer les badges d’articles).
    • runs-on : Pour exécuter vos jobs, GitHub vous fournit des runners hébergés — des machines virtuelles jetables. Windows, macOS et distributions Linux sont disponibles. Plus de détails sur les runners disponibles ici : https://github.com/actions/runner-images
    • steps : Comme précisé plus haut, on demande à GitHub Actions d’exécuter les jobs étape par étape.
    • uses: actions/checkout@v4 : Ce job copie le dépôt courant dans la machine (runner) où s’exécute le job. Plus de détails dans les commentaires du code.
    • id : Ce champ sert à récupérer les valeurs renvoyées par le job dans $GITHUB_OUTPUT. Elles sont utilisées dans l’étape de génération des badges avec steps.count.outputs.published et steps.count.outputs.total.
    • shell : Simple à comprendre, alors voici une liste des shells disponibles :
      • bash : Bash standard
      • sh : shell POSIX simple
      • pwsh : PowerShell Core (multi-plateforme)
      • powershell : PowerShell Windows
      • cmd : interpréteur Windows classique
      • python : (via une astuce, pour exécuter du code Python direct)

Je vous fais grâce de l’explication détaillée des scripts, mais retenons que chaque push lance :

  • un job qui compte,
  • un job qui édite les badges avec le résultat du décompte,
  • puis un job qui commit/pousse ces badges dans le dépôt.

L’heure du test 🤸🏼‍♀️

Votre YAML se trouve dans le bon dossier (.github/workflows) de votre dépôt local, alors un simple push sur votre repo public et voilà le workflow qui entre en action !

Je commit articles-badge.yml avec Source Control de Visual Studio Code
Ici je commit le YML sur mon repo avant de sync ➡ Étape 1
Screen de GitHub Actions et du workflow en cours d’exécution.
Le workflow s’exécute dans GitHub Actions ➡ Étape 2
Screen de GitHub Actions et du workflow en erreur.
Parfois on commet des erreurs 🙈 Erreur ❌
Screen de GitHub Actions et du détail de l’erreur du workflow.
Les détails de l’erreur Erreur ❌
Screen de GitHub Actions et du workflow réussi.
Après avoir revu l’indentation, le workflow passe ✨ ➡ Étape 3
Screen de GitHub Actions et des détails du workflow.
Les détails sur le déroulement du workflow ➡ Étape 3.1
Les badges dans le README.
Le résultat en image Résultat ✔

Conclusion

On part d’un geste manuel (mettre à jour le README) et on le transforme en pipeline reproductible : un push → un workflow GitHub Actions calcule des métriques (nombre d’articles, nombre de mots), génère des artefacts (badges) et effectue un commit dans le dépôt, avec des droits minimaux (contents: write).

C’est l’esprit DevOps : automatisation, mise sous version, feedback rapide et mesure.

Appliqué à la doc, c’est du DocOps. 🐱‍💻

Fil d'Ariane 💃