chess diagnostic with claude & stockfish
  • Python 94.6%
  • CSS 3.8%
  • HCL 0.7%
  • Dockerfile 0.4%
  • Shell 0.4%
  • Other 0.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
chipster b9ffa21d00
Some checks failed
Build & push Docker image / build (push) Failing after 5m4s
coach: pre-vol d'authentification claude en tete de run
Le 2026-09-02, le coach a ingere les parties, lance ~40 min de Stockfish
par joueur, puis decouvert que le CLI n'etait pas authentifie. Un ping
haiku de deux secondes avant la boucle rend la panne lisible tout de
suite et evite de brasser trois comptes pour rien.

preflight_auth() ne leve jamais. En cas d'echec, `_run_all` notifie
"Coach non demarre — claude indisponible" avec la cause et sort en 1.
Sauter un jour est sans consequence : la fenetre n'avance que sur un run
'done', les parties seront reprises au passage suivant.

Fixture autouse dans tests/conftest.py : sans elle, tout test appelant
`_run_all` partait en subprocess vers la vraie CLI (suite passee de 9 s
a 82 s, et resultat dependant de la machine).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XP5xCMda6LKaE4krgnn3F3
2026-09-08 10:07:58 +02:00
.forgejo/workflows ci: container catthehacker (docker-cli préinstallé pour login-action) 2026-04-21 19:43:35 +02:00
chessia coach: pre-vol d'authentification claude en tete de run 2026-09-08 10:07:58 +02:00
docker chore(coach): base SQLite persistée sous /data/coach 2026-07-17 06:20:30 +02:00
docs/superpowers docs: plan — arbitrages du scan pré-vol 2026-08-02 14:20:06 +02:00
infra/module docs(infra): corriger le healthcheck et le volume dans main.tf 2026-08-02 23:08:03 +02:00
tests coach: pre-vol d'authentification claude en tete de run 2026-09-08 10:07:58 +02:00
.dockerignore feat: containerisation — Dockerfile + pipeline Forgejo Actions (v0.3.9) 2026-04-21 12:51:13 +02:00
.env.example docs: préciser que les chemins /data/coach sont des défauts codés en dur 2026-08-02 23:08:07 +02:00
.gitignore docs(coach): spec de refonte des pages /coach 2026-07-19 14:31:54 +02:00
CLAUDE.md docs: corriger le module qui appelle _call_claude dans CLAUDE.md 2026-08-02 23:07:48 +02:00
Dockerfile fix(docker): pointer le HEALTHCHECK sur /healthz plutôt que sur / 2026-08-02 22:26:28 +02:00
pyproject.toml feat(admin): CLI chessia-admin (add/list/passwd/disable/enable) 2026-08-02 14:28:48 +02:00
README.md docs: documenter le widget de suggestion 2026-08-03 12:53:34 +02:00

ChessIA

Pipeline d'analyse pédagogique de parties d'échecs en français, cible FIDE 1600+.

Flux : PGN → Stockfish (multi-PV, seuils adaptatifs) → détection des coups critiques → Claude (routage Opus/Sonnet selon sévérité) → rapport HTML interactif avec graphique d'éval, mini-viewers et explications structurées.

Deux modes d'utilisation :

  • CLI (chessia partie.pgn -o rapport.html)
  • Web (chessia-web) — formulaire d'upload + historique + suivi live + notifications push

Installation

python -m venv .venv && source .venv/bin/activate
pip install -e ".[web]"                    # extras [web] pour le serveur
cp .env.example .env                       # renseigner STOCKFISH_PATH

Prérequis

  • Stockfish UCI avec NNUE (tablebases recommandées). vendor/stockfish/stockfish par défaut.
  • CLI claude (Claude Code) authentifiée via l'abonnement Max — le pipeline passe par claude -p (pas l'API Anthropic).

Variables d'environnement (.env)

Variable Défaut Rôle
STOCKFISH_PATH — Chemin du binaire UCI (obligatoire)
STOCKFISH_HASH_MB 128 Hash table Stockfish en Mo (par process ; l'analyse force Threads=1 pour la reproductibilité, parallélisme par process)
CLAUDE_CONCURRENCY 3 Appels Claude parallèles
CLAUDE_MODEL_BLUNDER opus Modèle pour blunders / occasions manquées
CLAUDE_MODEL_FORCE — Override tous les modèles (debug)
CHESSIA_REPORTS_DIR ~/.chessia-reports Dossier des rapports
CHESSIA_WEB_HOST / CHESSIA_WEB_PORT 127.0.0.1:8000 Écoute du serveur web
CHESSIA_ACCOUNTS_DB /data/coach/accounts.db Base des comptes, sessions et consommation de tokens (voir § Comptes)
CHESSIA_USERS_DIR /data/coach/u Dossier des bases de coaching, une par joueur (<login>.db)
CHESSIA_COOKIE_INSECURE — Mettre à 1 pour tester en local sur http:// sans TLS (le cookie de session perd son flag Secure). Ne jamais positionner en production : sans TLS, un cookie sans Secure circule en clair
NTFY_SERVER / NTFY_TOPIC / NTFY_USER / NTFY_PASSWORD — Notifs push début/fin de diag (voir ntfy.sh)

Usage

CLI

chessia partie.pgn -o rapport.html                   # analyse les deux camps
chessia partie.pgn --side white -o rapport.html      # limiter au côté Blancs

Serveur web

chessia-web                   # uvicorn sur 127.0.0.1:8000

Tout le site est derrière une session (middleware à liste blanche dans chessia/authweb.py) : sans compte, seules /login, /logout, /healthz et /static/ répondent. Voir § Comptes pour créer le premier compte.

  • /login, /logout : formulaire de connexion / déconnexion
  • /healthz : sonde de santé, publique, répond ok sans authentification
  • / : redirige vers /coach/ (rapport du jour), la porte d'entrée du site
  • /analyser : formulaire d'upload (fichier PGN ou coller du texte, drag & drop), onglet « Analyse » de la nav /coach — plus un analyseur public, il faut une session
  • /history : tableau des diagnostics avec filtres et polling live
  • /watch/<id> : suivi live d'un diag en cours (progress bar + timeline + logs colorés)
  • /report/<id> : rapport final avec TOC sticky, coups critiques repliables, export PDF
  • /example : redirige vers un rapport terminé aléatoire du joueur connecté (démo sans uploader son PGN)

Régénérer des rapports existants

Quand l'UI évolue, régénérer les rapports terminés avec le nouveau template (le numéro de version d'origine est préservé dans le rapport) :

python -m chessia.regen                    # tous les jobs done
python -m chessia.regen --dry-run          # aperçu
python -m chessia.regen --job <id>         # un seul job

Le script crée un report.html.bak.<timestamp> avant chaque overwrite.

Comptes utilisateurs

Multi-utilisateur depuis la branche multi-utilisateur (02/08/2026) : les comptes sont créés à la main, il n'y a ni inscription libre ni réinitialisation de mot de passe. Sur une installation neuve, personne ne peut se connecter tant qu'un compte n'a pas été créé — c'est la première chose à faire après le déploiement, pas une étape optionnelle.

# Installation vraiment neuve, sans base mono-utilisateur à reprendre — premier
# compte, administrateur :
chessia-admin add timothth TimothTh --admin

# Cas de ce déploiement : reprise de l'ancienne base mono-utilisateur (le compte
# hérite en plus des parties et rapports déjà accumulés). `migrate-legacy` sort
# en erreur (« base introuvable ») s'il n'y a pas de coach.db historique à
# reprendre — ce n'est PAS la commande d'une installation neuve :
chessia-admin migrate-legacy --login timothth --chesscom TimothTh --admin

# Ajouter un ami :
chessia-admin add bob BobChess --ntfy-topic chessia-bob

--ntfy-topic mérite d'être posé dès la création : sans lui, les notifications de coaching du nouveau joueur retombent sur le topic ntfy global (NTFY_TOPIC), c'est-à-dire le téléphone du propriétaire. chessia-admin ntfy-topic <login> [topic] corrige après coup.

Révoquer un accès : chessia-admin disable <login> (bloque les nouvelles connexions) puis chessia-admin logout <login> (coupe les sessions déjà ouvertes). Voir chessia/admin_cli.py pour l'ensemble des sous-commandes (list, passwd, enable).

Coach

Deuxième pipeline, indépendant du premier : au lieu d'analyser un PGN ponctuel apporté par l'utilisateur, chessia-coach tourne en tâche de fond (cron), ingère automatiquement les parties rapides jouées sur Chess.com, les fait analyser par Stockfish, puis produit un rapport de coaching quotidien et cumulatif via un unique appel claude -p. Les pages sont montées sous /coach/* dans le même serveur FastAPI (chessia.web:app).

Une base SQLite par joueur (schéma v8), sous CHESSIA_USERS_DIR/<login>.db — le pseudo Chess.com vient du compte (chessia-admin add <login> <pseudo>), il n'y a plus de variable d'environnement pour le fixer globalement. accounts.db (§ Comptes) est une base à part, qui ne contient aucune donnée de partie.

chessia-coach run                        # ingestion + analyse + rapport, tous les comptes actifs
chessia-coach run --user bob             # ne rejouer qu'un seul joueur
chessia-coach ingest --since <epoch> --user bob   # étapes séparées, pour debug
chessia-coach analyze --user bob
chessia-coach report --user bob

Sans --user, les sous-commandes manuelles (ingest/analyze/report/condense/ backfill-openings) portent sur le premier compte actif — l'administrateur s'il y en a un (active_users trie is_admin DESC, login ASC), sinon simplement le premier login actif par ordre alphabétique ; run sans --user boucle en série sur tous les comptes actifs.

Pages /coach/*

Route Contenu
/coach/ Rapport du jour (le plus récent), navigation vers /coach/report/<id>
/coach/history Index chiffré de tous les rapports (date, parties, ΔElo, V/D/N)
/coach/postit Mur de pense-bêtes : les recommandations du coach, par catégorie, cochables
/coach/progress Série temporelle par jour (parties, victoires, Elo)
/coach/consignes Contraintes actives transmises au coach, gérées ligne à ligne (ajout/édition/archivage)
/coach/admin Suggestions des joueurs à traiter, puis consommation de tokens par joueur (24 h / 7 j / 30 j) — réservé à l'administrateur (require_admin)
/feedback Formulaire de suggestion (hors préfixe /coach) : accessible depuis la pastille présente sur toute page passée par layout.page(), y compris /analyser

Ces pages n'ont pas de contrôle d'accès propre : elles s'appuient entièrement sur la session du middleware (chessia/authweb.py) — il n'y a plus de périmètre réseau (l'allowlist IP nginx qui restreignait /coach au LAN/tailnet a été supprimée au déploiement). Chaque joueur ne voit que sa propre base ; /coach/admin vérifie en plus is_admin.

Toute route d'écriture de ce routeur (et de chessia/feedback.py) porte la dépendance verifier_origine (chessia/authweb.py, partagée par les deux routeurs), qui vérifie en outre l'origine de la requête, car l'authentification par session ne protège pas de la CSRF : c'est le navigateur légitime du joueur déjà connecté qui émettrait la requête à son insu en visitant un site tiers, cookie de session compris. Le risque n'est pas réseau mais un vecteur de prompt-injection : le texte des consignes est réinjecté verbatim dans le prompt du run de 6 h, dans une section présentée au modèle comme des contraintes prioritaires — un site tiers pourrait ainsi piloter le coach. Sec-Fetch-Site fait foi (same-origin et none seuls acceptés), avec repli sur Origin vs Host ; une requête sans aucun des deux en-têtes (curl, script) est acceptée, car un tel client doit de toute façon déjà présenter un cookie de session valide. Un refus renvoie 403.

Suggestions des joueurs

Une pastille fixe en bas à droite, sur toute page passée par layout.page() (rapport, historique, /analyser…), ouvre un petit formulaire (texte + envoi) sans quitter la page — et fonctionne aussi sans JavaScript : la pastille est un vrai lien vers /feedback (chessia/feedback.py), une page à formulaire plein qui porte le chemin d'origine en query. Le message est stocké dans la table feedback d'accounts.db (auteur, date, page, texte, statut), et déclenche une notification ntfy immédiate vers l'administrateur. Celui-ci les lit et les marque traitées sur /coach/admin, dans une section dédiée au-dessus des tableaux de consommation — ni la lecture ni le marquage ne sont accessibles à un compte non administrateur.

Citations de coups

Dans le markdown produit par le modèle, un coup ne s'écrit jamais en texte libre : il doit utiliser le jeton [[m:<game_id>:<ply>]], où game_id (id complet ou ses 8 premiers caractères) et ply sont recopiés tels quels depuis les données Stockfish fournies dans le prompt. Le serveur (chessia/coach/render.py) résout chaque jeton en interrogeant la base et affiche à la place un bloc repliable (<details class="cite">) avec le coup, sa sévérité, le temps passé, un diagramme SVG (coup joué en rouge, meilleur coup Stockfish en vert) et un lien vers la partie sur Chess.com. Un jeton dont l'id ou le ply ne correspond à rien en base s'affiche « coup cité, partie inconnue » plutôt que de faire échouer le rendu. Les rapports produits avant cette fonctionnalité ne contiennent pas de jetons : ils continuent de s'afficher en texte brut, sans échiquier.

Recommandations : catégories et priorité

Chaque recommandation appartient à une catégorie parmi une liste fermée — opening / middlegame / endgame / general — imposée au modèle dans le prompt système ; toute autre valeur reçue est ramenée à general (normalize_category dans chessia/coach/store.py). Le modèle peut aussi marquer une recommandation comme prioritaire (priority: true) : ces recommandations s'affichent en tête du mur /coach/postit, dans un bandeau à part, mais le serveur ne garde jamais plus de 3 recommandations dans ce bandeau, même si le modèle en marque davantage.

Le prompt du run quotidien transmet toutes les recommandations, cochées comprises, avec leur status et leur checked_at : sans cela le modèle ne voit plus une recommandation cochée, ne peut plus la référencer par son id, et la recrée à l'identique au run suivant. Une recommandation cochée par le propriétaire n'est rouverte que si le modèle la signale délibérément persistante (still_failing: true à côté de refers_to_reco_id) ; une simple référence met seulement à jour la trace du dernier rapport et conserve checked_at et times_repeated (_apply_recommendations dans chessia/coach/pipeline.py).

Consignes utilisateur

Plusieurs consignes actives coexistent : toutes sont transmises au coach à chaque run de 6 h, sous forme de liste numérotée (queries.current_instructions), pas seulement la dernière saisie. Elles sont injectées telles quelles dans le prompt (chessia/coach/prompt.py::build_prompt), sous un en-tête qui précise au modèle qu'il s'agit de contraintes primant sur ses habitudes de coaching, pas de données à interpréter.

/coach/consignes les gère ligne à ligne (chessia/coach/web.py) :

  • Ajouter une nouvelle consigne (POST /coach/consignes) ;
  • Modifier le texte en place d'une consigne active (POST /coach/consignes/<id>/edit) — ce n'est pas un versionnement : le texte précédent n'est pas conservé ;
  • Archiver une consigne (POST /coach/consignes/<id>/archive) : elle disparaît de la liste active et n'est donc plus transmise au coach, mais aucune suppression dure — elle reste consultable et réactivable dans la section repliable en bas de page ;
  • Réactiver une consigne archivée (POST /coach/consignes/<id>/reactivate).

Chaque consigne porte un status (active / archived) et un updated_at. À l'initialisation d'une base (ou lors de sa migration vers ce schéma), une consigne inaugurale est semée automatiquement en active (volume minimal de parties à recommander par jour).

Pipeline

Phase 1 — Stockfish scan

Analyse chaque demi-coup avec multipv=2 (détecte les coups brillants : meilleur coup dans une position où les candidats sont proches).

Phase 2 — Re-analyse des coups critiques

Pour chaque coup flaggé blunder / mistake / inaccuracy / missed_win, ré-évalue avec multipv=3 pour fournir les 3 candidats à Claude.

Phase 3 — Explications Claude

Pour chaque coup critique, prompt structuré en 5 sections :

  1. Diagnostic — nom précis du motif tactique/positionnel
  2. Analyse comparative des 3 candidats Stockfish
  3. Ligne principale détaillée sur 6 à 10 demi-coups avec éval [+0.53], [+M3]
  4. Raison profonde (principe échiquéen violé)
  5. Exercice d'entraînement ciblé

Le prompt est enrichi par phase (ouverture / milieu / finale) et inclut :

  • Code ECO + nom d'ouverture (détection offline via chessia/data/eco.json)
  • Features positionnelles calculées (pions passés/doublés/isolés, paire de fous, contrôle centre)
  • Top 5 moments de tension de la partie
  • 15 derniers demi-coups d'historique

Catégories de coups

Catégorie Seuil NAG
blunder ≥ 200 cp de perte ?? ($4)
mistake ≥ 100 cp ? ($2)
inaccuracy ≥ 50 cp ?! ($6)
missed_win gain décisif lâché (+300 → <150) !? ($5)
brilliant meilleur coup dans position contestée (gap top1-top2 ≤ 60 cp) !! ($3)

Les seuils sont adaptatifs : ils montent avec la décisivité de la position via une sigmoid win_prob(cp) — moins de faux positifs sur positions déjà gagnées/perdues.

Rapport HTML

  • Autonome : styles inlinés, échiquier interactif via CDN lichess-pgn-viewer
  • Graphique d'éval SVG de la partie avec marqueurs colorés sur les coups notables
  • Coups critiques repliables (<details> natifs), sommaire sticky à gauche
  • Navigation clavier ↑ ↓ / j k pour sauter de coup en coup
  • Mini-viewer Lichess par bloc critique avec la variante Stockfish (PV 14 demi-coups)
  • Cases mentionnées par Claude mises en surbrillance sur le diagramme
  • Export PDF via window.print() (mise en page impression optimisée)
  • Light mode (toggle ☾/☀ avec persistance localStorage)

Architecture

chessia/
├── __main__.py            # CLI entrypoint
├── web.py                 # Serveur FastAPI (historique, watch, notifs, regen)
├── authweb.py             # Middleware de session, /login, /logout, /healthz, voir § Comptes
├── accounts.py            # Comptes, sessions, comptage des tokens (accounts.db)
├── admin_cli.py           # CLI chessia-admin (add | list | passwd | disable | enable | logout | ntfy-topic | migrate-legacy)
├── pipeline.py            # Orchestration : engine + claude + rapport
├── engine.py              # Wrapper Stockfish (python-chess UCI)
├── critical.py            # Détection (blunder/mistake/inaccuracy/missed_win/brilliant)
├── explain.py             # Appels Claude via subprocess claude -p
├── cache.py               # Cache FS des explanations (sha256 fen|ply|model)
├── eco.py                 # Détection offline ECO + nom d'ouverture
├── position_features.py   # Features positionnelles (pions, roi, centre, fous)
├── progress.py            # ETA (EMA) + cancel/pause contextvars
├── report_html.py         # Rendu rapport (graphique, TOC, mini-viewers, print CSS)
├── board_svg.py           # Diagramme SVG avec highlighted squares
├── regen.py               # Script CLI regen rapports (avec backup)
├── data/eco.json          # Base ECO (3700 entrées, lichess-org/chess-openings, CC0)
├── coach/                 # Pipeline coach (ingestion Chess.com + rapport quotidien), voir § Coach
│   ├── run.py             # CLI chessia-coach (run | ingest | analyze | report | condense | backfill-openings), --user <login>
│   ├── store.py           # Schéma SQLite + migrations + Store (une base par joueur)
│   ├── queries.py         # Lecture seule pour les pages /coach
│   ├── prompt.py          # System prompt + appel claude -p + parse JSON
│   ├── render.py          # Markdown → HTML nettoyé + résolution des citations
│   ├── admin_view.py      # Rendu de /coach/admin (consommation de tokens par joueur)
│   └── web.py             # Routes FastAPI /coach/*
└── static/                # Design system partagé (styles.css + theme.js)

Voir CLAUDE.md pour les conventions de développement et les règles non-évidentes depuis le code.

Déploiement Docker

Image multi-stage (build Stockfish from source + Python + Node.js pour Claude Code CLI).

Build local

docker build -t chessia:dev .

Run

Cet exemple écoute en clair sur 8000, sans TLS devant. Le cookie de session est Secure par défaut (chessia/authweb.py) : sur http://, le navigateur le jette silencieusement et la connexion semble échouer sans aucun message. CHESSIA_COOKIE_INSECURE=1 (voir § Variables d'environnement) désactive ce flag pour ce cas précis — à retirer dès qu'un reverse-proxy TLS est devant, ce qui est le cas en prod (voir Infra).

docker run -d \
  --name chessia \
  -p 8000:8000 \
  -v chessia-reports:/data/reports \
  -v chessia-cache:/data/cache \
  -v chessia-coach:/data/coach \
  -v chessia-claude:/home/chessia/.claude \
  -e CLAUDE_CREDENTIALS_JSON="$(cat ~/.claude/.credentials.json)" \
  -e CHESSIA_COOKIE_INSECURE=1 \
  -e NTFY_SERVER=https://ntfy.exemple.com \
  -e NTFY_TOPIC=chessia-diag \
  -e NTFY_USER=chessia \
  -e NTFY_PASSWORD=... \
  git.greil.fr/chipster/chessia:latest

Il n'y a pas encore de compte à ce stade (§ Comptes utilisateurs) : les commandes chessia-admin tournent dans le container, par exemple docker exec chessia chessia-admin add timothth TimothTh --admin.

Volumes à persister

Mount Contenu
/data/reports Rapports HTML + meta.json des diagnostics
/data/cache Cache des explications Claude (évite les re-appels)
/data/coach accounts.db (comptes, sessions, consommation de tokens) + une base de coaching par joueur sous u/<login>.db. Sans ce volume nommé, Docker crée un volume anonyme : ça fonctionne, mais rien ne garde le lien avec le container si on le recrée sans y penser — nommer le volume évite de perdre tous les comptes et tout l'historique de coaching par erreur
/home/chessia/.claude OAuth refresh tokens Claude Code (mis à jour automatiquement par le CLI). Persister ce volume évite d'avoir à ré-injecter CLAUDE_CREDENTIALS_JSON à chaque redémarrage tant que le refresh token reste valide

Secrets de run

  • CLAUDE_CREDENTIALS_JSON : contenu du fichier ~/.claude/.credentials.json de l'utilisateur Claude Code Max. L'entrypoint l'écrit dans /home/chessia/.claude/.credentials.json au démarrage.
  • NTFY_* : optionnel, pour les push mobiles (voir section Env ci-dessus).

Déploiement via tofu-maison — module non utilisé, non à jour

Ce module n'est pas le chemin de déploiement en production. La prod tourne via un compose Docker + Ansible dans le dépôt d'infrastructure séparé (Infra), pas via ce module OpenTofu. infra/module/ n'a pas été touché par le passage au multi-utilisateur (45 commits) : il ne monte aucun volume nommé sur /data/coach. L'image déclare ce chemin en VOLUME (Dockerfile), donc Docker y crée quand même un volume anonyme plutôt que d'écrire dans la couche writable — accounts.db et les bases de coaching de tous les joueurs survivent tant que le container vit, mais deviennent orphelins (plus aucun nom pour les retrouver) à la prochaine recréation (nouvelle image, tofu apply, etc.). Son healthcheck sonde aussi toujours / avec curl -fsS, qui ne considère qu'un code ≥ 400 comme un échec : / répond désormais 302 (redirection vers /login) pour un visiteur sans session, et curl -fsS accepte un 3xx — la sonde reste donc « healthy » même si tout ce qui est derrière le login est cassé, un faux positif strictement pire qu'une sonde qui remonterait unhealthy. Ne pas l'exécuter en le croyant à jour — voir l'en-tête du fichier.

Ce qui suit décrit le module tel qu'il existait avant le multi-utilisateur, conservé pour mémoire si quelqu'un le remet à niveau un jour.

Côté tofu-maison, dans docker/services.tf :

module "chessia" {
  source = "git::ssh://git@git.greil.fr/chipster/chessia.git//infra/module?ref=main"

  image         = "git.greil.fr/chipster/chessia:latest"
  external_port = 8000

  claude_credentials_json = var.chessia_claude_credentials_json
  ntfy_server             = "https://ntfy.exemple.com"
  ntfy_topic              = "chessia-diag"
  ntfy_user               = "chessia"
  ntfy_password           = var.chessia_ntfy_password
}

Puis tofu -chdir=docker init && tofu -chdir=docker apply.

Un workflow .forgejo/workflows/deploy.yml dans ce repo trigger automatiquement un apply de tofu-maison quand :

  • le module infra/module/** change (push main)
  • le build Docker termine avec succès (nouveau tag à tirer)
  • déclenché manuellement depuis l'UI Forgejo

Secret repo supplémentaire requis :

Secret Usage
TOFU_MAISON_TOKEN Token Forgejo avec write:repository sur mat/tofu-maison (pour déclencher son workflow apply.yml)

CI Forgejo Actions

Workflow dans .forgejo/workflows/docker-build.yml :

  • Trigger : push sur main, tag v*, ou déclenchement manuel
  • Build : image AMD64 (linux/amd64)
  • Push : git.greil.fr/chipster/chessia avec tags sha-<short>, latest (main), <version> (tags semver)
  • Cache layers : stockées en registry (:buildcache)
  • Runner : self-hosted

Secrets Forgejo à configurer dans les settings du repo :

Secret Usage
REGISTRY_USERNAME User pour push vers le registry Forgejo
REGISTRY_TOKEN Token d'accès avec scope write:package

Licence

Code : usage personnel. data/eco.json dérivé de lichess-org/chess-openings sous CC0.