Retour au portfolio

Projet personnel / Validation logicielle et pipelines de build

Preflight

Preflight vérifie qu’une machine et une modification respectent les exigences du projet avant un commit ou un build. Il s’adresse aux studios de jeux et aux entreprises de logiciel en général : SDK absents, fichiers trop volumineux et violations de politique sont détectés par des règles explicites, avec un diagnostic indiquant quoi corriger.

Pourquoi l’exécuter avant un commit ?

Une texture source ou un binaire généré trop volumineux peut entrer dans Git et n’être découvert qu’en CI. Le stage pre-submit vérifie les fichiers modifiés avant l’envoi : l’auteur reçoit le chemin, la limite, la taille constatée et une action corrective tant que la modification est facile à ajuster. Un hook de commit et la CI peuvent appeler la même logique ; l’équipe doit configurer ce hook.

Déterministe signifie que les mêmes fichiers, environnement inspecté, règles, politique et target donnent le même verdict et le même ordre de constats. La décision repose sur des conditions vérifiables, comme comparer des octets à une limite, sans interprétation par IA. Cela ne garantit pas un build réussi ou un logiciel fonctionnel : les prérequis connus sont vérifiés plus tôt. Durées et identifiants d’exécution varient.

  • C#
  • .NET
  • JSON
  • SARIF
  • Windows
Diagnostic illustratif utilisant une règle de taille de fichier de la documentation publique. Il s’agit d’un exemple de lecture, pas d’une capture d’exécution.
preflight run --stage pre-submit --changed-from origin/main --platform win64

core.presubmit.large-file   Failed
  at        Art/Characters/hero_diffuse.tga
  expected  <= 2,621,440 bytes
  actual    11,400,000 bytes
  fix       Retirez le fichier du contrôle de version ou demandez au responsable de la pipeline de revoir la limite.

preflight explain core.presubmit.large-file --platform win64

Le fichier contient 11 400 000 octets, au-delà de la limite Win64 de 2 621 440 octets. La règle compare sa taille à maxBytes ; elle ne juge ni la qualité artistique de la texture ni la justesse du code. preflight explain permet de vérifier pourquoi cette limite s’applique.

01 / Contexte

Retours tardifs, erreurs en cascade et scripts divergents

Dans un studio, il peut s’agir d’un asset au-delà du budget ; dans une entreprise de logiciel, d’un SDK absent, d’un chemin interdit ou d’un artefact de build ajouté au dépôt. Ces conditions peuvent être vérifiées avant les étapes coûteuses. Réserver la validation à la CI retarde le diagnostic jusqu’à l’exécution d’un job ; maintenir un script local différent ajoute une source de divergence.

J’ai séparé la vérification, compilée en C#, de sa politique JSON pour garder une seule logique et faire varier les exigences par projet. Les responsables de l’infrastructure définissent règles, limites et blocages, puis publient un paquet versionné. Les développeurs installent ce paquet et exécutent l’outil sans écrire les vérifications. La CI reste le point de contrôle de l’équipe et exécute la même logique.

Ce qui se passe lors d’une exécution pre-submit

  1. 01

    Résolution de la politique

    preflight run --stage pre-submit --changed-from origin/main --platform win64 sélectionne la pipeline déclarée par le checkout et une version installée acceptée. L’argument de plateforme choisit sa couche de politique.

  2. 02

    Sélection des règles et dépendances

    --changed-from origin/main définit la référence du diff Git ; ce n’est pas un filtre limité aux fichiers indexés. Le stage pre-submit sélectionne les règles racines et leurs prérequis, même s’ils appartiennent à d’autres stages.

  3. 03

    Exécution et rapport

    Les règles s’exécutent par niveau du graphe avec un parallélisme borné. Le rapport indique emplacement, attendu, constaté et correction. preflight explain core.presubmit.large-file --platform win64 montre l’origine de la limite appliquée.

workspace
Vérifie la machine et l’environnement de développement.
pre-submit
Vérifie les fichiers modifiés selon la politique du projet.
build-readiness
Vérifie les prérequis d’un build.

La commande évalue les fichiers suivis dans le diff Git par rapport à la référence choisie. Elle ne crée ni commit ni hook automatiquement.

02 / Conception et architecture

Décisions d’architecture et leurs conséquences

Règle et politique

Une implémentation, des limites différentes par projet

Une règle sait comment vérifier quelque chose. Sa politique décide de son activation, de ses paramètres, de sa sévérité et de son effet bloquant. Deux projets utilisent le même assembly avec des budgets de taille différents, sans dupliquer le code. La logique se teste séparément et les exigences peuvent évoluer sans recompiler l’outil.

Exemple de politique JSON : une règle avec une limite générale et une autre pour Win64.
{
    "schemaVersion": 1,
    "pipeline": "projecta",
    "rules": {
        "core.presubmit.large-file": {
            "settings": {
                "maxBytes": 5242880
            }
        }
    },
    "targets": {
        "win64": {
            "rules": {
                "core.presubmit.large-file": {
                    "settings": {
                        "maxBytes": 2621440
                    }
                }
            }
        }
    }
}

Sans target explicite, maxBytes vaut 5 242 880 (5 MiB). Avec --platform win64, il vaut 2 621 440 (2,5 MiB). L’implémentation C# reste identique ; l’exigence change. Cet extrait illustre la sélection des paramètres sans reproduire le manifeste de distribution.

Graphe d’exécution

Dépendances exécutées par niveaux du graphe

Les règles déclarent leurs dépendances et s’exécutent par niveaux topologiques. Les vérifications indépendantes partagent un niveau ; les suivants attendent leurs prérequis. Si la vérification de la toolchain échoue, une sonde de compilation dépendante peut être ignorée au lieu de produire un autre échec prévisible.

L’attribution de la cause racine traverse les dépendances ignorées intermédiaires. Le message final désigne la toolchain absente, donnant un problème à résoudre plutôt qu’une liste de symptômes. L’étape sélectionne les racines du graphe sans éliminer les dépendances d’autres étapes.

J’ai choisi une barrière entre niveaux pour simplifier la propagation des échecs et la coordination. Une règle lente retarde le niveau suivant même si une partie pourrait déjà commencer. Ce coût est délibéré : l’exécution est plus facile à auditer et le rapport est ordonné par niveau et ID, indépendamment de l’ordre de fin des tâches.

Deux contrôles indépendants

blocking et gating répondent à des questions différentes

Une convention de nommage peut bloquer un submit sans rendre la compilation inutile. Une sonde facultative peut être un prérequis technique sans que son échec rejette la soumission. Un seul booléen ne représente pas ces deux cas. Preflight sépare blocking, qui affecte le verdict et le code de sortie, de gating, qui arrête les règles dépendantes. La sévérité reste le niveau de communication.

Provenance de la politique

Origine et priorité des valeurs de politique

Les politiques héritent de paramètres, appliquent des cibles explicites de plateforme/configuration et scellent des clés contre les modifications en aval. Les scellés s’accumulent dans la chaîne d’héritage : un projet ne peut pas retirer silencieusement une restriction de l’organisation. Les overlays locaux sont exclus de la CI.

La commande explain conserve l’origine des valeurs effectives : paquet, fichier, ligne et valeurs remplacées. Les axes de cible doivent être fournis explicitement pour correspondre à un bloc. Une configuration par défaut ne sélectionne donc pas silencieusement une autre politique de production.

La priorité doit être visible, car une valeur JSON ne suffit pas à expliquer la configuration effective. Sans provenance, enquêter sur une différence entre une machine et la CI obligerait à reconstruire l’héritage à la main. preflight explain fournit ces données à partir de la résolution elle-même.

Frontière des plugins

Un contrat commun aux règles intégrées et externes

Les règles implémentent IValidationRule via Preflight.Abstractions. Les accès aux fichiers, processus, modifications et politiques passent par les services de RuleContext, permettant des tests sans workspace réel. L’assembly des règles intégrées n’a pas de dépendance privilégiée envers Core.

Les plugins utilisent des contextes d’assembly séparés et collectables, en partageant le contrat avec l’hôte. Les versions de dépendances peuvent coexister ; les identifiants dupliqués et contrats incompatibles sont refusés explicitement. Cet isolement concerne les dépendances, pas la sécurité : les plugins restent du code approuvé par le responsable du pipeline.

Distribution

Règles et politique distribuées dans un paquet versionné

Un paquet contient la politique, les assemblies des règles et un manifeste avec des empreintes SHA-256 par fichier et une plage de contrats compatible. L’ordre stable des entrées et des horodatages fixes rendent ses octets reproductibles. L’installation vérifie le paquet avant de le valider dans le stockage installé.

Le checkout déclare une plage de versions acceptées ; une machine peut fixer une version pour revenir en arrière. Installer un paquet ne déplace pas ce pin. Preflight ne récupère aucune mise à jour seul : la distribution utilise le canal d’artefacts du studio sans modifier les règles par surprise.

Résultats fiables

Des états de résultat aux significations distinctes

Passed, Warning, Failed, Errored, Skipped et NotApplicable ont des significations distinctes. Une règle sans élément applicable ne revendique pas un succès ; un crash se distingue d’un défaut du workspace. Console, JSON et SARIF présentent les mêmes données. Les codes de sortie distinguent une modification bloquée d’une configuration invalide et d’une erreur interne.

L’historique est un NDJSON local en ajout seul ; les statistiques de durée exigent assez d’observations. L’outil peut chronométrer un build, mais mesurer sa durée ne prouve ni sa validation ni le fonctionnement du logiciel.

Le déterminisme concerne le verdict et l’ordre des constats à entrées identiques, environnement inspecté et version de pipeline compris. La durée et le runId varient par construction ; comparer les octets exige de contrôler ces champs. Une même politique sur des machines avec des SDK différents peut correctement donner des résultats différents.

Cache incrémental

Cache conditionné par l’identité des entrées

Le cache est activé explicitement et réservé aux règles fournissant une empreinte de leurs entrées. La clé comprend aussi la politique effective, l’étape, la cible, la génération du contrat et l’identité de l’assembly. Modifier une limite ou recompiler un plugin invalide donc l’ancien résultat. Les résultats en cache sont signalés, sans les présenter comme une nouvelle vérification.

Cette décision exige que l’auteur de la règle décrive les entrées pertinentes. Pour une sonde incapable de représenter son environnement de manière fiable, relancer est préférable à réutiliser une preuve incomplète. Le cache est désactivé par défaut.

03 / Dans le code

Frontières entre contrat, exécution, règles et interface

Preflight.Abstractions

Le vocabulaire des plugins : descripteurs, résultats, contexte et interfaces de services. Il dépend de la bibliothèque de base, gardant le contrat léger pour les règles externes.

Preflight.Core

Résolution des politiques, graphes, exécution, chargement des plugins, cache et historique. Calcule les données du rapport sans dépendance vers la CLI.

Preflight.Rules

Les vérifications intégrées utilisent le contrat public, comme un plugin de l’équipe. Elles montrent la validation du workspace, des modifications et des prérequis du build sans intégrer les exigences de chaque projet dans l’outil.

Preflight.Cli

Commandes, parsing, empaquetage et rendu des résultats. La ligne de commande héberge le core ; une autre intégration peut consommer ses données sans analyser le texte du terminal.

Comment le comportement est vérifié

Le dépôt combine tests unitaires, contrat de plugins, sortie console exacte et scénarios Gherkin exécutant le binaire publié. Des tests de frontières empêchent les règles intégrées de dépendre de Core et Core de dépendre de la CLI. Le script vérifie le formatage, compile avec les avertissements traités comme erreurs, exécute les suites et collecte la couverture. Ces contrôles concernent l’outil, séparément des règles de validation définies pour chaque projet de logiciel ou de jeu.

04 / Travail en cours

Implémentation actuelle et limites du contrat

Travail en cours

Preflight est public sous licence MIT et reste en version inférieure à 1.0. L’implémentation actuelle cible .NET 10 et est développée sous Windows. Règles, politiques, paquets et rapports fonctionnent ensemble ; l’API publique peut encore changer.

La direction consiste à renforcer vérifications et preuves pour les pipelines de jeux et le développement logiciel en général. Les règles spécifiques appartiennent à l’équipe qui connaît les exigences de son projet. Compilation et tests restent dans leurs outils ; une IDE ou une ferme de builds pourrait héberger le même core. Cette page décrit la CLI existante.

Explorer le projet

Le README couvre installation, usage quotidien, création de règles, politiques et distribution des pipelines. Le code montre comment ces contrats se relient à l’exécution et aux rapports.

Lire la documentation sur GitHub

Contact

Québec, QC, Canada / Ouvert aux opportunités

Je suis ouvert aux possibilités en développement logiciel, en programmation d'outils et de gameplay, ainsi qu'aux postes d'artiste 3D junior. Si mon expérience peut répondre aux besoins de votre équipe, je serai heureux d'échanger avec vous sur mes réseaux sociaux.