Guide du développeur — VEAF Mission Creation Tools¶
Ce guide s'adresse aux développeurs qui souhaitent contribuer au code source de VEAF Mission Creation Tools, créer de nouvelles versions ou étendre le framework.
Table des matières¶
- Vue d'ensemble de l'architecture
- Structure du dépôt
- Environnement de développement
- Scripts Lua runtime
- Outils Python
- Build et publication
- Mode développeur
- Tests
- Portes de qualité
- Contribuer
Vue d'ensemble de l'architecture¶
Le projet comporte deux couches complètement séparées :
flowchart TD
subgraph DT["DESIGN-TIME — Python"]
tools["veaf-tools.exe<br/>— manipulation des .miz"]
updater["veaf-tools-updater.exe<br/>— gestion des versions"]
build["veaf-build<br/>— pipeline de build"]
end
DT -->|produit| zip(["published.zip"])
zip -->|consommé par| RT
subgraph RT["RUNTIME — Lua dans DCS World"]
scripts["veaf-scripts.lua<br/>— les modules concaténés"]
config["veaf-config.lua<br/>— config générée des modules (depuis mission.yaml)"]
custom["mission-script.lua<br/>— code Lua custom spécifique à la mission"]
end
- Runtime (
src/scripts/veaf/) — modules Lua chargés dans les missions DCS - Design-time (
src/python/veaf-tools/) — outils CLI Python pour la manipulation des fichiers.miz
Structure du dépôt¶
VEAF-Mission-Creation-Tools/
├── veaf_build/ # CLI veaf-build (orchestrateur build & publication)
├── build-and-release.py # Shim de rétrocompatibilité (utiliser veaf-build à la place)
├── src/
│ ├── scripts/veaf/ # Modules Lua runtime
│ └── python/veaf-tools/ # Code source Python CLI
│ ├── veaf-tools.py # Point d'entrée
│ ├── veaf_libs/ # Utilitaires partagés (logger, progress, miz)
│ ├── mission_tools/ # Lecture/écriture .miz
│ └── *_injector/ # Un dossier par commande CLI
├── published/ # Sortie Lua compilée
├── dist/ # Sortie .exe PyInstaller
├── build/ # Espace de travail de build temporaire
├── test/
│ ├── lua/ # Tests unitaires Lua
│ └── python/ # Tests unitaires Python
├── doc/ # Documentation
├── .backlog/ # Backlog des lots (PRD + tickets)
└── .github/
└── workflows/ # CI/CD GitHub Actions
Environnement de développement¶
Deux options de setup sont disponibles. Le DevContainer est recommandé pour les nouveaux contributeurs : il garantit un environnement identique à la CI.
Option A — DevContainer (recommandé)¶
Le dépôt inclut une configuration .devcontainer/ qui fournit un environnement pré-configuré, sans installation manuelle : Python 3.13, Lua 5.1, StyLua 2.4.0, Poetry et toutes les extensions VS Code sont déjà installées.
VS Code Dev Containers (Docker local) :
- Installer Docker Desktop et l'extension Dev Containers
- Ouvrir le dossier du dépôt dans VS Code
- Appuyer sur
Ctrl+Shift+P→ Dev Containers: Reopen in Container - Attendre la construction du conteneur et la fin de
poetry install— l'environnement est prêt
GitHub Codespaces (navigateur, sans installation locale) :
- Sur la page du dépôt, cliquer sur Code → Codespaces → New codespace
- L'environnement se construit automatiquement — ouvrir un terminal et commencer à travailler
Dans les deux cas, poetry install --without build --all-extras s'exécute automatiquement à la première ouverture.
Option B — Setup manuel (Windows)¶
1. Python 3.11+¶
Télécharger l'installateur depuis python.org (version 3.13 recommandée) ou via winget :
winget install --id Python.Python.3.13
Important : pendant l'installation graphique, cocher "Add Python to PATH". Sans cette case,
pythonetpipne seront pas trouvés dans le terminal.
Vérification :
python --version # Python 3.11 ou supérieur attendu
2. Poetry¶
Poetry gère les environnements virtuels Python et les dépendances du projet. La méthode recommandée est via pipx, qui isole Poetry dans son propre environnement :
python -m pip install pipx
pipx ensurepath # ajoute ~/.local/bin au PATH — redémarrer le terminal ensuite
pipx install poetry
Vérification :
poetry --version
poetry installcrée automatiquement un virtualenv isolé dans le projet. Toutes les commandes du projet s'exécutent ensuite avec le préfixepoetry run <commande>.
3. Git¶
winget install --id Git.Git
Ou télécharger depuis git-scm.com.
4. Lua 5.1 (pour les tests unitaires)¶
Lua 5.1 est requis pour exécuter les tests localement. La version 5.1 est obligatoire — les versions 5.2+ ne sont pas compatibles avec le code DCS.
Via Scoop (gestionnaire de paquets Windows recommandé) :
# Installer Scoop si pas encore présent
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
# Installer Lua 5.1
scoop install lua51
Si une autre version de Lua est déjà installée via scoop,
lua51remplace son shimlua(le paquet en déclare un). Le shimlua51reste disponible pour les deux, etpoetry run test-luasait le trouver — mais la commandeluanue de votre terminal aura changé de version.
Alternativement, télécharger un binaire depuis LuaBinaries (lua-5.1.x_Win64_bin.zip), extraire et ajouter le dossier au PATH système.
Vérification :
lua -v # Lua 5.1.x attendu
Si ce n'est pas une 5.1, poetry run test-lua refuse de s'exécuter et affiche ce qu'il a trouvé :
lancer la suite sous 5.4 produit des dizaines d'échecs qui ressemblent à des régressions du code
VEAF et n'en sont pas.
5. StyLua 2.4.0 (qualité du code Lua)¶
StyLua formate le code Lua. La version 2.4.0 est imposée par la CI — toute autre version fera échouer le job de formatage.
Télécharger stylua-windows-x86_64.zip depuis la page de release v2.4.0, puis installer :
# Créer le dossier cible
New-Item -ItemType Directory -Force "$HOME\.local\bin"
# Extraire et placer l'exécutable (adapter le chemin selon où le zip a été extrait)
Copy-Item "chemin\vers\stylua.exe" "$HOME\.local\bin\stylua.exe"
# Vérifier
~/.local/bin/stylua.exe --version # stylua 2.4.0 attendu
6. GitHub CLI — optionnel, uniquement pour publier des versions¶
winget install --id GitHub.cli
gh auth login
Cloner et initialiser le projet¶
Une fois tous les prérequis installés :
git clone https://github.com/VEAF/VEAF-Mission-Creation-Tools.git
cd VEAF-Mission-Creation-Tools
git checkout develop
# Installer toutes les dépendances Python
poetry install
# Vérifier que tout fonctionne
poetry run test-lua # tests Lua (requiert Lua 5.1)
poetry run pytest # tests Python
Pour compiler les exécutables Windows (
veaf-tools.exe, etc.), ajouter le groupebuild:poetry install --with build
Dépendances Python¶
| Package | Utilité |
|---|---|
typer |
Framework CLI |
rich |
Interface terminal (barres de progression, tableaux) |
pyyaml |
Chargement des fichiers de config |
luadata |
Sérialisation/désérialisation Lua |
pyinstaller |
Compilation des exécutables Windows |
pillow |
Traitement d'images (icônes météo) |
Scripts Lua runtime¶
Structure des modules¶
Chaque module Lua suit ce modèle :
moduleName = {}
moduleName.Id = "MODULE_ID"
-- moduleName.LogLevel = "trace" -- décommenter pour augmenter la verbosité
veaf.loggers.new(moduleName.Id, moduleName.LogLevel)
function moduleName.initialize()
-- s'enregistrer auprès des marqueurs, radio, gestionnaire d'événements
end
function moduleName.start()
-- démarrer les watchdogs, tâches planifiées
end
Ordre de chargement¶
veaf.lua— doit être en premierveafEventHandler.luaveafMarkers.lua,veafRadio.lua,veafSecurity.lua- Tous les autres modules (dans n'importe quel ordre)
Journalisation¶
veaf.loggers.get(moduleName.Id):info("Message")
veaf.loggers.get(moduleName.Id):debug("Debug: %s", variable)
veaf.loggers.get(moduleName.Id):trace("Trace: %s", veaf.lp(table))
Niveaux de log : error (1) → warning (2) → info (3) → debug (4) → trace (5). Par défaut : info (3).
info ou debug ? info est lu par quelqu'un qui n'a rien cherché : un module qui se charge,
une version, un réglage valable pour toute la mission, un refus, quelque chose qui ne marchera pas.
debug est le commentaire d'une action en cours (« cap 25 retenu à 1,054x », « 3 candidats
trouvés », « véhicule de tête à portée »), lu par quelqu'un qui a monté le niveau parce qu'il cherche
quelque chose. Le test : un créateur de mission qui ne débogue pas voudrait-il cette ligne ? Si
non, c'est debug. Une commande lancée par un pilote est déjà tracée en info par veafRemote ;
le module qui l'exécute n'a pas à la répéter à ce niveau.
Pour les arguments coûteux à évaluer, utiliser veaf.lp() (proxy lazy — stringifié uniquement si le niveau est actif).
Pour augmenter la verbosité pour une mission au moment du build (global, intégré dans le .miz), ajouter global_log_level dans mission.yaml :
global_log_level: debug
Pour le contrôle par module au moment du build, utiliser la section modules :
modules:
SPAWN:
logLevel: debug
RADIO:
logLevel: trace
Cela génère des appels veaf.setConfig("MODULE_ID", "logLevel", "...") dans veaf-config.lua. Ou utiliser --log-modules SPAWN,RADIO sur la CLI pour réduire au silence tout le reste.
Pour le contrôle par module au runtime (sans rebuild), ajouter l'appel Lua directement dans mission-script.lua :
veaf.loggers.get("SPAWN"):setLevel("debug", true) -- force=true contourne le cap BaseLogLevel
Planification d'une fonction¶
Ne pas appeler timer.scheduleFunction ni mist.scheduleFunction. Utiliser veaf.scheduleFunction,
qui pose la tâche sur le timer natif de DCS en gardant les commodités attendues : répétition, heure
d'arrêt, arguments en table, et une tâche qui échoue sans emporter les autres.
-- une seule fois, dans 30 secondes
local id = veaf.scheduleFunction(maFonction, { arg1, arg2 }, timer.getTime() + 30)
-- toutes les 10 secondes, pendant une heure
veaf.scheduleFunction(maFonction, {}, timer.getTime() + 10, 10, timer.getTime() + 3600)
veaf.removeFunction(id) -- annule ; renvoie false si la tâche a déjà tourné
L'implémentation vit dans veafScheduler.lua. Il n'y a pas de boucle de scrutation : chaque tâche
est un appel planifié natif, réarmé en renvoyant sa prochaine échéance, donc rien ne tourne quand rien
n'est dû.
Maths, vecteurs et conversions¶
Ne pas appeler mist.utils.* ni mist.vec.*. Les fonctions vivent dans veafMath.lua, derrière des
façades veaf.* :
veaf.metersToNM(d) veaf.NMToMeters(d) veaf.metersToFeet(d) veaf.feetToMeters(d) veaf.mpsToKnots(v)
veaf.vecAdd(a, b) veaf.vecScalarMult(v, k) veaf.vecMag(v) veaf.get2DDist(p1, p2)
veaf.makeVec3(p, alt) veaf.makeVec2(p) veaf.getDir(v, point) veaf.getNorthCorrection(point)
veaf.deepCopy(t)
Trois pièges, dans l'ordre où ils mordent :
makeVec3/makeVec2traduisent entre les deux conventions de coordonnées de DCS — liredocs/agents/dcs-coordinates.mdavant de les utiliser. Se tromper ne lève aucune erreur, seulement une position à cent kilomètres.- Pas de
veaf.toRadianniveaf.toDegree: ce sontmath.radetmath.deg, de la bibliothèque standard Lua. - Pas de
veaf.rounddansveafMath: il est dansveaf.luadepuis toujours, et il est identique à celui de MiST.
Rendu texte d'une position¶
veafGeo.lua assemble les chaînes de coordonnées, derrière deux façades :
veaf.toStringLL(lat, lon, acc) -- "42 21.00'N⇥ 43 34.07'E" (minutes décimales)
veaf.toStringLL(lat, lon, acc, true) -- "42 21' 00\"N⇥ 43 34' 04\"E" (degrés/minutes/secondes)
veaf.toStringMGRS(coord.LLtoMGRS(lat, lon), 3) -- "38T KM 123 679"
La géodésie reste celle de DCS (coord.LOtoLL, coord.LLtoMGRS) ; ce module ne fait que le texte.
veafGeo porte aussi les zones et les polygones :
veaf.zoneToVec3("NOM DE ZONE") -- centre de la zone, ou nil si elle n'existe pas
veaf.getAvgPos({ "unite1", "unite2" }) -- position moyenne, nil si aucune n'existe
veaf.getAvgGroupPos("nom du groupe")
veaf.pointInPolygon(point, sommets) -- accepte les deux formes de coordonnées
veaf.getUnitsInPolygon({ "u1", "u2" }, sommets)
Et l'allocation d'identifiants d'unité vit dans veafMissionDb.lua : veaf.getNextUnitId().
Ces chaînes partent dans les rapports F10 et les briefings, donc elles sont figées par des tests à
chaîne littérale. Le port a repris MiST au caractère près, bizarreries comprises ; corriger l'une
d'elles change ce que lisent les pilotes, et s'est donc décidé à part. Les deux l'ont été :
FIX-DMS-MINUTE-CARRY (en DMS, une minute atteignant 60 se reporte sur le degré, comme le faisait
déjà le mode décimal) et FIX-ZERO-HEMISPHERE (une position à exactement zéro s'affiche N/E, le
test d'hémisphère étant passé de > 0 à >= 0).
Ce que la mission contient¶
veafMissionDb.lua répond à trois questions, et n'accumule rien d'autre.
L'instantané de l'éditeur — chaque groupe et chaque unité posés dans l'éditeur de mission, lus une
seule fois dans env.mission. Un enregistrement de mission n'est pas un objet vivant : il existe
pour une unité qui n'est pas encore apparue et pour une unité déjà détruite. C'est exactement ce que
Unit.getByName ne sait pas faire, et la raison d'être de l'index.
veaf.getUnitRecord(unitName) -- x, y, alt, type, groupName, coalition, coalitionId, category…
veaf.getGroupRecord(groupName)
veaf.getGroupRecordById(groupId)
veaf.getAllUnitRecords() -- pour itérer
veaf.getAllGroupRecords()
veaf.getBullseye("blue") -- directement depuis env.mission
La liste des joueurs — les slots de l'éditeur, plus les slots dynamiques de DCS, qui n'existent
dans aucun env.mission. C'est le seul des trois qui se rafraîchit, et il le fait quand on le lit, pas
sur une horloge :
veaf.isHumanUnit(unitName)
veaf.getAllHumanRecords()
Le registre des noms — ce que VEAF a nommé, pour qu'un groupe puisse réapparaître sous un nom déjà utilisé :
veaf.takeSpawnedName(name)
veaf.releaseSpawnedName(name)
veaf.isNameTaken(name)
Les anciennes façades veaf.mist.getGroupData, isHumanUnit, getAllUnitData, getAllGroupData,
getAllHumanUnitData et getGroupById existent toujours et redirigent ici ; du code neuf appelle
veaf.* directement. veaf.mist.getUnitData a été supprimée : elle n'avait aucun appelant.
Outils Python¶
Architecture CLI¶
Chaque sous-commande de veaf-tools.exe est implémentée comme un package *_injector/ :
weather_injector/
├── weather_worker.py # Point d'entrée (méthode run())
├── weather_manager.py # Logique de transformation des données
├── models.py # Définitions des dataclasses
└── weather_README.py # Chaînes d'aide/documentation
Pattern de journalisation¶
from veaf_libs.logger import logger, console
logger.info("Traitement de la mission...")
logger.debug("Informations détaillées")
logger.warning("Attention")
logger.error("Échec", raise_exception=True)
Ajouter un nouvel outil¶
- Créer
src/python/veaf-tools/new_feature_injector/ - Implémenter
new_feature_worker.pyavec une méthoderun() - Enregistrer la commande avec
typerdansveaf_tools/commands/, puis la classer dans un groupe deveaf_tools/command_tree.py— une commande non classée fait échouer les tests, parce qu'elle disparaîtrait de--help. Il n'y a rien à ajouter dansveaf-tools.py: ce point d'entrée délègue àveaf_tools.app.main()et ne connaît aucune commande. - Ajouter le schéma de configuration YAML dans
models.py
Aides de test partagées¶
test/python/testlib/ contient les aides utilisées par plusieurs fichiers de test. Le dossier est
sur le pythonpath de pytest, donc ses modules s'importent par leur nom :
from mission_builder_factory import make_worker
make_worker(**overrides) construit un MissionBuilderWorker sans exécuter __init__ — celui-ci
lit mission.yaml, résout le chemin des scripts et vérifie la présence du chargeur sur le disque,
ce qu'un test unitaire d'une seule méthode veut éviter. Tous les attributs que __init__ affecte
sont présents avec une valeur neutre ; le test ne nomme que ce qui l'intéresse :
worker = make_worker(mission_yaml={"dcs_bridge": {"enabled": True}}, dev_mode=True)
Aucun accès disque : mission_folder vaut None par défaut. Quand un dossier est fourni,
output_mission en dérive (<mission_folder>/out.miz). Une clé inconnue est refusée
(TypeError) plutôt que de créer en silence un attribut que personne ne lit ; les remplacements de
méthode s'affectent sur le worker retourné, pas via make_worker.
Ajouter un champ à MissionBuilderWorker.__init__ impose d'ajouter une entrée dans
init_field_defaults(). Ce n'est pas à retenir :
test/python/mission_builder/test_mission_builder_factory_contract.py lit les affectations
self.<champ> de __init__ et échoue en nommant le champ manquant et le fichier à corriger.
Build et publication¶
Build local¶
# Build (compile Lua + construit les .exe)
poetry run veaf-build build --version <version>
Ce que cela fait :
1. Valide les prérequis (Git, Python, PyInstaller)
2. Concatène les modules Lua → published/veaf-scripts.lua
3. Construit veaf-tools.exe et veaf-tools-updater.exe via PyInstaller
4. Crée published.zip avec tous les artefacts + somme SHA256
Publier une version¶
Utiliser le prompt .prompts/generate-release-notes.md pour lancer la préparation de release de façon interactive. Il guide à travers :
1. Extraction des changements depuis [Unreleased] dans CHANGELOG.md
2. Interview de consolidation (thème, breaking changes, highlights)
3. Rédaction et validation de RELEASE_NOTES.md
4. Clôture administrative (version CHANGELOG, pyproject.toml, ROADMAP)
5. Commandes git à copier-coller
Flow de release (git flow)¶
L'assistant AI gère : créer release/x.y.z depuis develop, commiter tous les fichiers de release,
et ouvrir la PR vers master — jamais vers develop.
La PR de release se merge par un vrai commit de fusion, pas en
squash. Unsquashréécrit la release en un commit neuf :masteretdevelopcessent de partager leur histoire,developapparaît définitivement « N commits en avance », le tag devient inatteignable depuismasteret les fusions suivantes lèvent des conflits artificiels. C'est arrivé sur la 6.11.0, réparé par une fusion en retour.
Après le merge de la PR, le développeur exécute :
# 1. les DEUX tags, sur master
git checkout master
git pull origin master
git tag published-vx.y.z # → exécutables, published.zip, kit de capture
git tag vx.y.z # → documentation versionnée + alias « latest »
git push origin published-vx.y.z vx.y.z
# 2. fusion en retour, pour que develop partage l'histoire de master
git checkout develop
git pull origin develop
git merge origin/master
git push origin develop
Attention : pousser le tag est irréversible — uniquement après le merge de la PR.
Les deux tags, ou la documentation reste sur la version précédente.
published-v*publie les binaires ;vx.y.zdéploie la documentation et déplace l'aliaslatest. Ne pousser que le premier livre des binaires dont la documentation n'est pas publiée — c'est ce qui est arrivé à la 6.11.0, dont le site est resté sur la 6.10.0 faute de tagv6.11.0.Ne pas sauter la fusion en retour : sans elle
developetmasterdivergent, et chaque release suivante devient plus difficile à fusionner.
Pousser le tag déclenche le workflow CI release, qui va :
1. Construire veaf-tools.exe, veaf-tools-updater.exe et published.zip
2. Créer la GitHub Release en utilisant RELEASE_NOTES.md tel quel depuis le commit tagué
3. Uploader tous les artefacts et déplacer le tag flottant published-latest
Important :
RELEASE_NOTES.mddoit être commité et à jour sur le commit tagué — la CI le prend verbatim, sans modification.
Modèle de sécurité¶
veaf-build calcule le SHA256 de published.zip
↓
SHA256 stocké avec le ZIP dans la GitHub Release
↓
veaf-tools-updater.exe télécharge les deux fichiers
↓
Somme de contrôle vérifiée avant extraction
↓
✅ Intégrité garantie
Tests¶
Lancer tous les tests¶
poetry run test-lua
Code de sortie 0 = tous passent, 1 = échecs.
Fonctionne sur Windows, Linux et dans le DevContainer (détection automatique de lua5.1 / lua51 / lua / chemin Windows de secours). Chaque candidat est interrogé avec lua -v : un interpréteur 5.2+ est refusé, avec les instructions d'installation, plutôt qu'utilisé — sinon les incompatibilités de la 5.4 ressemblent à des régressions du code VEAF.
Exécution filtrée¶
poetry run test-lua --filter spawn
poetry run test-lua --filter combat
Couverture¶
poetry run test-lua --coverage
Affiche un tableau de couverture ligne par ligne. Nécessite luarocks install luacov (pré-installé dans le DevContainer). Voir TESTING.md pour plus de détails.
Suite unique¶
lua test/lua/test_veafSpawn.lua
Infrastructure¶
- Framework : luaunit (intégré dans
test/lua/luaunit.lua) - Stubs DCS :
test/lua/dcs_mocks.lua— stubs pour tous les espaces de noms de l'API DCS - Chargeur de modules :
test/lua/veaf_loader.lua - Aucune installation DCS requise
Référence complète des tests : Guide de tests
Portes de qualité¶
Avant chaque commit sur des fichiers Lua¶
# Vérifier le formatage (équivalent CI)
~/.local/bin/stylua.exe --check src/scripts/veaf/ test/lua/
# Corriger automatiquement
~/.local/bin/stylua.exe src/scripts/veaf/ test/lua/
# Analyse statique
luacheck src/scripts/veaf/ --config .luacheckrc
Version StyLua : 2.4.0 (imposée par le job CI StyLua Formatting).
Luacheck est imposé par le job CI Luacheck.
Jobs CI¶
| Job | Ce qu'il vérifie |
|---|---|
Lua Unit Tests |
Toutes les suites de tests passent |
Luacheck |
Aucune variable globale non définie, variable inutilisée ni shadowing dans src/scripts/veaf/ |
StyLua Formatting |
Aucune violation de formatage dans src/scripts/veaf/ et test/lua/ |
Lua Coverage |
Couverture ligne (luacov) au-dessus du plancher de cliquet (--cov-fail-under) — bloquant |
python-quality |
ruff lint + format (src/python/ test/python/ veaf_build/), mypy (src/python/veaf-tools), pytest |
exe-smoke |
L'exécutable empaqueté démarre (--help) et exécute une commande réelle — le seul job qui voit un défaut d'empaquetage, invisible depuis un checkout |
Docs Check |
Liens et ancres de la documentation, versions FR/EN, pages absentes du menu |
Release |
Déclenché sur push de tag published-v* — build et publication sur GitHub |
Tous les jobs CI doivent être verts avant qu'une PR puisse être mergée. Exception :
dcs-mock-coverage est en continue-on-error — informatif, il ne bloque pas le merge.
Sur develop, ces checks sont requis par une règle de protection de branche : c'est ce qui
permet l'auto-merge, qui attend alors vraiment le vert. Un check requis dont le workflow ne démarre
pas reste « Expected » indéfiniment et bloque la PR. C'est pourquoi Python Quality, Docs Check
et Support Bot démarrent sur toutes les PR : leur filtre de chemins est dans un job changes
(veaf_build/ci_path_gate.py), et un job qu'il écarte apparaît « Skipped », ce qu'un check requis
accepte. Pour modifier la liste de chemins d'un de ces workflows, modifiez à la fois on.push.paths
et le GATE_PATHS du job changes — test_ci_trigger_paths.py vérifie qu'ils restent identiques.
Avant un commit qui touche à la documentation¶
poetry run docs-check
Le job CI Docs Check lance exactement la même commande, qui enchaîne trois passes : la passe
principale sur doc/ (le tableau ci-dessous), une passe de liens relatifs sur le reste du dépôt
(.backlog/, docs/, les pages racine), et une passe de couverture documentaire (chaque capacité
définie par le code doit être nommée par sa page de référence). La passe principale refuse quatre
dérives qui, avant son existence, s'étaient accumulées silencieusement (voir le lot
DOC-AUDIT-PASS) :
| Vérification | Pourquoi |
|---|---|
lien relatif .md vers un fichier inexistant |
six liens renvoyaient un 404 en production |
| ancre inter-page inexistante dans la page cible | une renumérotation de sections avait laissé des liens derrière elle |
| ancre inter-page dérivée d'un titre | elle casse au premier reformulage et diffère entre FR et EN ; déclarez {#ancre} |
page FR sans version .en.md, ou absente du menu nav |
une page est restée non traduite des mois, servant du français sur l'URL anglaise |
Convention d'ancre : le nom de l'ancre est en anglais et identique dans les deux langues ; le titre affiché, lui, reste dans la langue de la page.
## Couverture {#coverage} <!-- FR : titre français, ancre anglaise -->
## Coverage {#coverage} <!-- EN : même ancre -->
Un lien inter-page vise alors toujours #coverage, quelle que soit la langue du lecteur.
La version affichée en en-tête des grosses références n'est pas écrite à la main : le dépôt garde un intervalle lisible (
6.11.x) et le workflow de publication la remplace par la version livrée (poetry run docs-stamp-version).
Republier la documentation d'une version déjà sortie¶
Relancer le build du tag ne suffit pas : il reconstruirait depuis le commit tagué, donc sans
un correctif arrivé après le tag. Utilisez le déclenchement manuel du workflow Deploy Docs :
| Champ | Valeur |
|---|---|
| branche | celle qui contient le correctif (develop) |
version |
la version à republier, par exemple 6.12.0 |
set_latest |
coché si cette version doit rester la latest du site |
La version saisie est aussi celle qui est tamponnée dans les pages — sans quoi republier la 6.12.0
depuis un dépôt en 6.12.1 estampillerait les pages 6.12.1. Laisser version vide redéploie
simplement l'alias dev.
Publier une nouvelle version¶
Pousser les deux tags depuis master, une fois la PR de release fusionnée — le workflow CI
Release fait le reste automatiquement :
git tag published-v<version> # → exécutables, published.zip, kit de capture
git tag v<version> # → documentation versionnée + alias « latest »
git push origin published-v<version> v<version>
Le processus complet (branche de release, cible de la PR, méthode de fusion, fusion en retour) est décrit dans Flow de release.
Mode développeur¶
Le mode développeur permet de tester des modifications locales de veaf-scripts.lua sans publier de version.
Lorsqu'il est activé, veaf-tools mission build lit les scripts depuis un clone local de VEAF-Mission-Creation-Tools
plutôt que depuis le dossier published/ livré avec veaf-tools.
Prérequis¶
- Cloner VEAF-Mission-Creation-Tools en local
- Construire le bundle Lua :
poetry run veaf-build build→ produitbuild/veaf-scripts.lua
Activation (ordre de priorité — premier trouvé appliqué)¶
| Priorité | Méthode | Effet |
|---|---|---|
| 1 | veaf-tools mission build --dev-mode |
Option CLI — définit dev_mode: true, persisté dans mission.yaml |
| 2 | mission.yaml build.dev_mode: true |
Config persistée — s'applique à chaque build |
| 3 | (défaut) | false — utilise les scripts publiés |
Ordre de résolution de scripts_path (emplacement du dépôt local) :
| Priorité | Source |
|---|---|
| 1 | Option CLI --scripts-path <chemin> |
| 2 | mission.yaml build.scripts_path |
| 3 | ~/veafmct.yaml scripts_path |
Lorsqu'ils sont passés via la CLI, dev_mode et scripts_path sont persistés dans mission.yaml.
La persistance réécrit uniquement la section build: : tout ce qui l'entoure — commentaires,
autres sections, y compris celles placées après elle — est conservé, et les fins de ligne
restent en LF. Jusqu'à la 6.15.2 ce n'était vrai qu'avant la section : le build tronquait le
fichier à son marqueur, donc un bloc écrit après build: disparaissait au build suivant, sans
un mot.
Effet sur le build¶
| Mode | Source des scripts |
|---|---|
dev_mode: false (défaut) |
published/src/scripts/veaf/veaf-scripts.lua (copie publiée) |
dev_mode: true |
<scripts_path>/build/veaf-scripts.lua (sortie du build local) |
Exemple de workflow¶
# 1. Modifier un module Lua
code src/scripts/veaf/veafSpawn.lua
# 2. Reconstruire le bundle Lua
poetry run veaf-build build
# 3. Builder une mission de test avec les scripts locaux
cd chemin/vers/ma-mission
veaf-tools mission build --dev-mode --scripts-path chemin/vers/VEAF-Mission-Creation-Tools
Contribuer¶
Git Flow¶
- Développement de fonctionnalité : créer
feature/xxxdepuisdevelop, ouvrir PR →develop - Corrections de bugs : créer
fix/xxxdepuisdevelop, ouvrir PR →develop - Hotfixes en production :
fix/xxxdepuismaster, PR →master - Versions :
release/X.Y.Z(sansv) depuisdevelop, PR →master
Convention de commit¶
type(scope): courte description
feat(spawn): ajouter le mode patrouille convoy
fix(qra): vérifier unit:isExist() avant unit:inAir()
chore(deps): mettre à jour luaunit à 3.4
docs(api): documenter les helpers tanker de veafMove
Types : feat, fix, chore, docs, test, refactor, style
Checklist Pull Request¶
- [ ] Tous les changements Lua passent
stylua --check - [ ] Tous les tests unitaires passent (
poetry run test-lua) - [ ] Les nouvelles fonctionnalités ont des tests dans
test/lua/ - [ ] Les changements d'API publique sont documentés dans
doc/LUA_API_REFERENCE.md - [ ]
CHANGELOG.mdmis à jour pour les changements visibles par les utilisateurs
Pour aller plus loin¶
- Référence API Lua — API publique complète des modules
- Guide de tests — détails de l'infrastructure de test
- Référence CLI — les 37 commandes de
veaf-toolset toutes leurs options - Feuille de route — travaux planifiés