Skip to content

sync-claude-docs

Publie le contenu Markdown de .claude/docs d'un projet vers le serveur MCP de documentation centrale (/docs/<CI_PROJECT_PATH_SLUG>/) via rsync+SSH, pour que les fichiers de contexte agent du projet soient queryables depuis tous les agents Spektrum.

Pas lié au release flow — peut être inclus seul.

Snippet d'include

yaml
include:
  - component: $CI_SERVER_FQDN/spektrum/ci-templates/sync-claude-docs@<version>
    inputs:
      stage: deploy

stages:
  - deploy

Remplacer <version> par un tag git du repo spektrum/ci-templates.

Inputs

InputRequisDéfautDescription
stageouiStage du job. Pas de défaut : le composant ne sait pas quels stages le projet déclare.

Trigger

yaml
rules:
  - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "web"
  - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    changes:
      - .claude/docs/**/*

Le job tourne uniquement sur la branche par défaut, dans deux cas :

  1. Automatiquement, sur un push branche-par-défaut qui modifie un fichier sous .claude/docs/**/*.
  2. Manuellement, en lançant une pipeline depuis l'UI GitLab (Build → Pipelines → "Run pipeline") ciblant la branche par défaut. Sert d'escape hatch (re-sync forcé après wipe serveur, après rotation des variables CI, etc.).

Il ne tourne jamais sur feature branches, MR pipelines ou pipelines planifiées — le serveur MCP partagé doit refléter l'état canonique (branche par défaut), pas du WIP de feature branch.

Ce que fait le job

  1. Installe rsync et openssh-client dans un conteneur Alpine.
  2. Configure la clé SSH privée et known_hosts depuis des variables CI/CD de type File (provisionnées au niveau instance).
  3. Lance :
    rsync -avz --delete \
      --include='*/' --include='*.md' --exclude='*' \
      -e "ssh -p $MCP_DOCS_SSH_PORT" \
      .claude/docs/ \
      "$MCP_DOCS_SSH_USER@$MCP_DOCS_SSH_HOST:/docs/$CI_PROJECT_PATH_SLUG/"
  4. Exit clean (exit 0) si le repo n'a pas de .claude/docs/ (sécurité, normalement le rules: changes empêche ce cas).

Variables CI/CD requises

Provisionnées au niveau instance GitLab — pas à configurer par projet :

VariableTypeDescription
MCP_DOCS_SSH_HOSTVariableHostname du serveur MCP (ex. mcp.internal.spektrum-suisse.ch).
MCP_DOCS_SSH_PORTVariablePort SSH (ex. 2223).
MCP_DOCS_SSH_USERVariableUser SSH du déployeur (ex. docs-deploy).
MCP_DOCS_SSH_KEYFileClé privée SSH du user de déploiement.
MCP_DOCS_KNOWN_HOSTSFileSortie de ssh-keyscan pour le host MCP (skippe le prompt TOFU).

Les deux dernières doivent être de type File (le job les consomme comme chemins, pas comme valeurs).

Layout sur le serveur

Les fichiers atterrissent sous :

/docs/<CI_PROJECT_PATH_SLUG>/
  ├── README.md
  ├── conventions/
  │   └── git.md
  └── runbooks/
      └── deploy.md

CI_PROJECT_PATH_SLUG est le group/project slugifié par GitLab, donc chaque projet a son sous-arbre isolé — pas de risque de collision.

Notes & gotchas

  • --delete est destructif côté serveur. Supprimer .claude/foo.md du repo le supprime du serveur MCP au prochain run. C'est voulu : le but est de mirrorer le repo, pas d'accumuler.
  • Seul *.md est transféré. Les autres formats sont filtrés par les règles --include/--exclude de rsync. Pour étendre, surcharger le job côté projet.
  • Pas de secret projet : tout est provisionné instance-wide. Le projet consommateur n'a aucun token/clé à gérer.
  • Image Alpine : le apk add domine le startup. Si la latence CI devient un sujet sur beaucoup de projets, pré-builder une image avec rsync + openssh-client baked.

Voir aussi

Contributors

No contributors

Changelog

No recent changes