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.
- 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
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
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
Deux options de setup sont disponibles. Le DevContainer est recommandé pour les nouveaux contributeurs : il garantit un environnement identique à la CI.
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.
Télécharger l'installateur depuis python.org (version 3.13 recommandée) ou via winget :
winget install --id Python.Python.3.13Important : 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 attenduPoetry 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 poetryVé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>.
winget install --id Git.GitOu télécharger depuis git-scm.com.
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 lua51Si 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 attenduSi 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.
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 attenduwinget install --id GitHub.cli
gh auth loginUne 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 PythonPour compiler les exécutables Windows (
veaf-tools.exe, etc.), ajouter le groupebuild:poetry install --with build
| 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) |
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
endveaf.lua— doit être en premierveafEventHandler.luaveafMarkers.lua,veafRadio.lua,veafSecurity.lua- Tous les autres modules (dans n'importe quel ordre)
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).
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: debugPour le contrôle par module au moment du build, utiliser la section modules :
modules:
SPAWN:
logLevel: debug
RADIO:
logLevel: traceCela 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 BaseLogLevelNe pas accéder à mist.DBs.* directement. Utiliser l'interface veaf.mist :
local unitData = veaf.mist.getUnitData(unitName)
local groupData = veaf.mist.getGroupData(groupName)
local isHuman = veaf.mist.isHumanUnit(unitName)
local allUnits = veaf.mist.getAllUnitData()
local groupById = veaf.mist.getGroupById(groupId)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
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)- Créer
src/python/veaf-tools/new_feature_injector/ - Implémenter
new_feature_worker.pyavec une méthoderun() - Enregistrer la commande dans
veaf-tools.pyavectyper - Ajouter le schéma de configuration YAML dans
models.py
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_workermake_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 (compile Lua + construit les .exe)
poetry run veaf-build build --version <version>Ce que cela fait :
- Valide les prérequis (Git, Python, PyInstaller)
- Concatène les modules Lua →
published/veaf-scripts.lua - Construit
veaf-tools.exeetveaf-tools-updater.exevia PyInstaller - Crée
published.zipavec tous les artefacts + somme SHA256
Utiliser le prompt .prompts/generate-release-notes.md pour lancer la préparation de release de façon interactive. Il guide à travers :
- Extraction des changements depuis
[Unreleased]dansCHANGELOG.md - Interview de consolidation (thème, breaking changes, highlights)
- Rédaction et validation de
RELEASE_NOTES.md - Clôture administrative (version CHANGELOG,
pyproject.toml, ROADMAP) - Commandes git à copier-coller
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 developAttention : 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 :
- Construire
veaf-tools.exe,veaf-tools-updater.exeetpublished.zip - Créer la GitHub Release en utilisant
RELEASE_NOTES.mdtel quel depuis le commit tagué - 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.
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
poetry run test-luaCode 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.
poetry run test-lua --filter spawn
poetry run test-lua --filter combatpoetry run test-lua --coverageAffiche 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.
lua test/lua/test_veafSpawn.lua- 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
# 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 .luacheckrcVersion StyLua : 2.4.0 (imposée par le job CI StyLua Formatting).
Luacheck est imposé par le job CI Luacheck.
| 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.
poetry run docs-checkLe 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).
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.
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.
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.
- Cloner VEAF-Mission-Creation-Tools en local
- Construire le bundle Lua :
poetry run veaf-build build→ produitbuild/veaf-scripts.lua
| 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.
| 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) |
# 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- 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
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
- 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
- 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 25 commandes de
veaf-toolset toutes leurs options - Feuille de route — travaux planifiés