projet crepp_git/crepp-projects/station-meteo/hardware/transmetteur/base/v1.1 · branche main
generate_TOC.py Voir sur GitLab
import re
import sys
import unicodedata


def slugify(title: str, seen: dict) -> str:
    """Reproduit l'algorithme d'ancre de GitHub (approximatif mais correct
    pour le français) : minuscules, accents retirés, ponctuation supprimée,
    espaces -> tirets, et suffixe -1/-2/... en cas de titres en double
    (comme le fait GitHub, sinon plusieurs entrées du sommaire pointeraient
    vers la même ancre, celle du premier titre)."""
    normalized = unicodedata.normalize('NFKD', title.lower())
    without_accents = ''.join(c for c in normalized if not unicodedata.combining(c))
    slug = re.sub(r'[^\w\s-]', '', without_accents)  # ponctuation retirée
    slug = re.sub(r'\s+', '-', slug).strip('-')

    if slug in seen:
        seen[slug] += 1
        return f"{slug}-{seen[slug]}"
    seen[slug] = 0
    return slug


def generate_markdown_toc(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()

    # Rechercher la section de sommaire existante
    toc_regex = r'<!-- ATOC -->.*?<!-- ATOC -->'
    toc_match = re.search(toc_regex, content, re.DOTALL)

    # Si une section de sommaire existe déjà, on l'enlève
    if toc_match:
        content = content.replace(toc_match.group(), '', 1)  # une seule occurrence

    # Parcourir le contenu pour identifier les en-têtes, en ignorant les blocs
    # de code (``` ... ```) : une ligne commençant par '#' à l'intérieur d'un
    # bloc de code (commentaire Python, shebang bash, etc.) n'est pas un titre.
    toc_lines = []
    seen_slugs = {}
    in_code_block = False
    for line in content.splitlines():
        stripped = line.strip()
        if stripped.startswith('```') or stripped.startswith('~~~'):
            in_code_block = not in_code_block
            continue
        if in_code_block:
            continue

        if line.startswith('#'):
            level = len(line) - len(line.lstrip('#'))
            title = line[level:].strip().strip('#').strip()
            if not title or title.lower() == "sommaire":  # Exclure "Sommaire" du ToC
                continue
            identifier = slugify(title, seen_slugs)
            toc_lines.append(f"{'  ' * (level - 1)}- [{title}](#{identifier})\n")

    # Créer le nouveau sommaire sans l'entrée "Sommaire"
    new_toc = "<!-- ATOC -->\n# Sommaire\n\n" + ''.join(toc_lines) + "\n<!-- ATOC -->"

    # Ajouter le nouveau sommaire en haut du fichier
    updated_content = new_toc + '\n' + content

    # Écrire le contenu mis à jour dans le fichier
    with open(file_path, 'w', encoding='utf-8') as f:
        f.write(updated_content)

    print(f"Sommaire mis à jour dans '{file_path}'.")


if __name__ == "__main__":
    if len(sys.argv) < 2:
        print(f"Usage : {sys.argv[0]} <fichier.md>", file=sys.stderr)
        sys.exit(1)

    try:
        generate_markdown_toc(sys.argv[1])
    except Exception as e:
        # On remonte une erreur explicite avec un code de sortie non-nul :
        # avant, l'exception était juste affichée et le script rendait la
        # main avec un code 0, donc la CI (set -euo pipefail) ne détectait
        # jamais un échec de mise à jour du sommaire.
        print(f"Erreur lors de la génération du sommaire : {e}", file=sys.stderr)
        sys.exit(1)