WIKI
AZ-400 DevOps Engineer

03 · Packages, versions et tests dans les pipelines

31 min de lecture Mis à jour le 21 octobre 2026
Sur cette page

Version : 1.1 · Date : 2026-10-22 · Domaine 3 de l'examen : Design and implement build and release pipelines (50 à 55 %)

En une phrase : un pipeline sérieux produit un binaire versionné une seule fois, le teste à plusieurs niveaux, le publie dans un feed (Azure Artifacts) ou un registre (GitHub Packages), puis ne le promeut que si des gates de qualité, de sécurité et de gouvernance sont au vert.

Du commit au package validé : versionner, tester, publier, promouvoir

Ce que l'examen demande#

Texte officiel du guide d'étude AZ-400 (compétences au 27/07/2026), blocs Design and implement a package management strategy et Design and implement a testing strategy for pipelines :

# Compétence mesurée (guide officiel, en anglais) Section
1 Recommend package management tools including GitHub Packages and Azure Artifacts 2
2 Design and implement package feeds and views for local and upstream packages 3
3 Design and implement a dependency versioning strategy for code assets and packages, including semantic versioning (SemVer) and date-based (CalVer) 4
4 Design and implement a versioning strategy for pipeline artifacts 5
5 Design and implement quality and release gates, including security and governance 6
6 Design a comprehensive testing strategy, including local tests, unit tests, integration tests, and load tests 7
7 Implement tests in a pipeline, including configuring test tasks, configuring test agents, and integration of test results 8
8 Implement code coverage analysis 9

Les pipelines eux-mêmes (YAML, triggers, agents, templates, environments) sont au chapitre 04, la rétention et les tests instables (flaky tests) au chapitre 06, l'analyse de sécurité du code et des dépendances au chapitre 07.


1. Les bases à maîtriser#

1.1 Le vocabulaire#

Terme Ce que c'est
Package Une unité réutilisable et versionnée (NuGet, npm, Maven, Python, Cargo, Universal Package, image de conteneur) consommée par d'autres projets
Feed Le conteneur de packages d'Azure Artifacts. Un feed peut héberger plusieurs types de packages
View Un sous-ensemble filtré des versions d'un feed (@local, @prerelease, @release) qu'on partage avec un public donné
Upstream source Une source que le feed interroge quand il ne contient pas le package (registre public, autre feed)
Pipeline artifact Le résultat d'un run (binaires, scripts, fichiers IaC) passé d'un job ou d'un stage à l'autre, ou d'un pipeline à l'autre
Quality gate / release gate Une condition automatique ou humaine qui autorise ou bloque la suite du pipeline

1.2 Deux familles d'artifacts à ne pas confondre#

  • Pipeline artifacts (publish, PublishPipelineArtifact@1) : liés à un run, pour déployer ce que ce run a construit. Ils ne comptent pas dans le stockage facturé d'Azure Artifacts.
  • Packages dans un feed : réutilisables entre projets, avec une version immuable, des views et des upstreams. Tous les packages comptent dans le stockage, y compris ceux sauvegardés depuis un upstream et ceux de la corbeille (Recycle Bin).

2. Choisir GitHub Packages ou Azure Artifacts#

GitHub Packages ou Azure Artifacts

2.1 GitHub Packages#

  • Registres : npm, RubyGems, Apache Maven, Gradle, NuGet, et le Container registry (ghcr.io) pour les images Docker et OCI.
  • Visibilité : un package publié dans un dépôt public est public ; dans un dépôt privé, il est partagé avec les collaborateurs ou l'organisation.
  • Permissions : héritées du dépôt, ou granulaires (par utilisateur ou organisation) selon le registre.
  • Authentification : dans GitHub Actions, le GITHUB_TOKEN publie les packages du dépôt du workflow (avec permissions: packages: write). Hors Actions, GitHub Packages n'accepte qu'un personal access token (classic) (pas de fine-grained PAT), avec les scopes read:packages (installer), write:packages (publier) et delete:packages (supprimer, avec read:packages). Visibilité d'un package : public ou private, réglée par un admin du package.
  • Facturation : les packages publics sont gratuits ; les privés ont un quota gratuit de stockage et de transfert selon le plan.

2.2 Azure Artifacts#

  • Types : NuGet, npm, Maven, Gradle, Python, Cargo et Universal Packages (n'importe quels fichiers, jusqu'à 4 TiB).
  • Upstream sources vers les registres publics et d'autres feeds, views pour la diffusion, retention policies.
  • Facturation au stockage : 2 GiB gratuits par organisation. Au-delà, on ne peut plus publier tant qu'on n'a pas configuré la facturation (Organization settings > Billing > Usage limit > No limit, pay for what you use).

2.3 La règle de choix#

Situation Choix
Code et CI dans GitHub, packages consommés surtout par GitHub Actions GitHub Packages
Images de conteneur produites dans GitHub GitHub Container registry (ghcr.io), ou Azure Container Registry pour les déployer dans Azure
CI dans Azure DevOps, besoin de cacher les registres publics (résilience, contrôle) Azure Artifacts avec upstream sources
Canaux de diffusion (préversion, validé) pour d'autres équipes Azure Artifacts avec views
Fichiers arbitraires versionnés (outils, jeux de données, binaires) Universal Packages (Azure Artifacts)

Piège : GitHub Packages n'a pas d'équivalent des upstream sources ni des views d'Azure Artifacts. Une question qui parle de « proxy des registres publics » ou de « promouvoir un package validé » vise Azure Artifacts.


3. Feeds, upstream sources et views#

Un feed, des upstream sources, des views

3.1 Créer un feed#

Artifacts > Create Feed : nom, Visibility (qui voit les packages), Scope (projet ou organisation) et la case Include packages from common public sources qui active les upstream sources publics. Microsoft recommande le scope projet.

Scope URL de la source Accès
Project-scoped contient le projet (…/{org}/{project}/_packaging/{feed}/…) il faut aussi l'accès au projet
Organization-scoped sans le projet toute l'organisation selon les rôles

Les public feeds dépendent des public projects. À partir de 2027, les projets publics existants deviennent privés ; leurs feeds publics ne seront plus accessibles publiquement.

3.2 Les upstream sources#

Le feed cherche toujours dans le même ordre : (1) les packages publiés dans le feed, (2) ceux déjà sauvegardés depuis un upstream, (3) chaque upstream, dans l'ordre configuré. C'est ce qui rend la restauration déterministe : NuGet, par exemple, interroge en parallèle toutes les sources d'un nuget.config et prend la première réponse, d'où des résultats imprévisibles.

Bonnes pratiques Microsoft :

  • un seul feed dans chaque fichier de configuration client (nuget.config, .npmrc, pip.conf), avec ses upstreams ;
  • ordonner les upstreams volontairement, surtout si l'organisation republie en interne des packages open source modifiés ;
  • pour un feed d'une autre organisation du même tenant Microsoft Entra : le locator azure-feed://organisation/projet/feed@view, et le propriétaire doit partager la view avec All feeds and people in organizations associated with my Microsoft Entra tenant.

Points chiffrés et comportements :

  • 20 upstreams au maximum par type de package et par feed ;
  • quand un package vient d'un upstream, Azure Artifacts en garde une copie : les builds continuent si le registre public tombe ;
  • le premier enregistrement depuis un upstream exige au moins Feed and Upstream Reader (Collaborator). Un simple Feed Reader ne peut consommer que ce qui est déjà sauvegardé ;
  • les snapshots Maven ne sont pas pris en charge dans les upstreams.

3.3 Les views#

  • Trois views par défaut : @local (vue par défaut : tout ce qui est publié et tout ce qui est sauvegardé depuis un upstream), @prerelease et @release, ces deux dernières pouvant être renommées ou supprimées.
  • On publie toujours dans le feed (donc dans @local), jamais directement dans une autre view, même si on change la view par défaut. On promeut ensuite : Package > Promote ou l'API REST (PATCH JSON Patch).
  • Pas de rétrogradation : une version promue ne revient pas à @local.
  • Les views sont en lecture seule et servent de canaux : @prerelease pour les premiers utilisateurs, @release pour tous.
  • Une view peut être partagée plus largement que le feed (Visibility de la view) : quelqu'un qui a accès à une view télécharge ses packages même sans droit sur le feed. Pour verrouiller, il faut restreindre le feed et ses views.
  • Promouvoir exige Feed Publisher (Contributor) ou Feed Owner.
# Consommer seulement les versions validées : la source pointe sur la view @release
#   https://pkgs.dev.azure.com/{organisation}/{projet}/_packaging/{feed}@release/nuget/v3/index.json

3.4 Rétention, suppression et immuabilité#

  • Immuabilité : une version publiée est réservée pour toujours. Impossible de republier 1.2.3, même après suppression.
  • Retention policies (Feed settings > Feed details > Enable package retention) avec deux réglages : Maximum number of versions per package et Days to keep recently downloaded packages. Une version n'est supprimée que si les deux conditions sont réunies (nombre de versions atteint et pas de téléchargement dans le délai).
  • Les versions promues dans une view sont exemptées de la rétention : promouvoir, c'est aussi protéger.
  • Le nettoyage tourne une fois par jour. Les versions supprimées vont dans la Recycle Bin, gardées 30 jours (et toujours facturées) ; seul un Feed Owner peut restaurer ou supprimer définitivement.
  • Selon l'écosystème : Deprecate (npm, avertissement), Unlist (NuGet, masqué des recherches), Yank (Cargo), Delete (corbeille). nuget.exe delete vers Azure Artifacts fait un unlist, pas une suppression.

Limites : 5 000 versions par package, 500 MiB par fichier (4 TiB pour les Universal Packages), 375 KB pour un package.json.

3.5 Les rôles du feed et l'accès des pipelines#

Les quatre rôles d'un feed Azure Artifacts

  • Project Collection Administrators et Azure Artifacts Administrators sont automatiquement Feed Owner de tous les feeds.
  • Le Build Service du projet ({Projet} Build Service ({Organisation})) et le Project Collection Build Service reçoivent par défaut Feed and Upstream Reader (Collaborator) : ils restaurent et sauvegardent depuis les upstreams, mais ne publient pas. Pour publier depuis un pipeline, passez le Build Service en Feed Publisher (Contributor).
  • Feed d'un autre projet : ajoutez le Build Service aux Readers du projet qui héberge le feed, et donnez-lui un rôle sur le feed.
  • Un run déclenché depuis un fork externe n'a pas accès aux secrets : l'authentification au feed échoue après coup.

Authentification dans Azure Pipelines :

steps:
- task: NuGetAuthenticate@1          # configure nuget.exe, dotnet et MSBuild pour toute la durée du job
- script: dotnet restore
- script: dotnet pack -c Release -o $(Build.ArtifactStagingDirectory) /p:Version=$(packageVersion)
- script: dotnet nuget push $(Build.ArtifactStagingDirectory)/*.nupkg --source "MonFeed" --api-key AzureArtifacts
  • NuGetAuthenticate@1 est la méthode recommandée : elle installe l'Azure Artifacts Credential Provider et vaut pour tout le job, y compris dans vos propres scripts. Pour un feed d'une autre organisation ou un dépôt tiers, déclarez une NuGet service connection dans nuGetServiceConnections.
  • Équivalents pour les autres écosystèmes : npmAuthenticate@0, PipAuthenticate@1, ou le flux UniversalPackages@0.
  • Hors pipeline : le Credential Provider en interactif, ou un service principal avec certificat par la variable ARTIFACTS_CREDENTIALPROVIDER_FEED_ENDPOINTS.

Dans GitHub Actions (publication npm vers GitHub Packages) :

permissions:
  contents: read
  packages: write                      # sans cette ligne, le GITHUB_TOKEN ne peut pas publier
jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          registry-url: https://npm.pkg.github.com
      - run: npm ci && npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Un package privé n'est téléchargeable par les workflows d'autres dépôts que si ceux-ci ont reçu la lecture dans Package settings > Manage Actions access.


4. Versionner le code et les packages : SemVer et CalVer#

Versionner le code, les packages et les artifacts de pipeline

4.1 Semantic Versioning (SemVer)#

Format MAJOR.MINOR.PATCH :

Partie On l'incrémente quand… Exemple
MAJOR un changement casse la compatibilité (MINOR et PATCH repartent à 0) 2.6.5 → 3.0.0
MINOR on ajoute une fonction compatible (PATCH repart à 0) 1.4.17 → 1.5.0
PATCH on corrige un bug compatible 1.0.0 → 1.0.1
  • Préversion : suffixe -alpha, -beta.1, -rc.2. Une préversion est inférieure à la version finale (2.0.0-rc.1 < 2.0.0). En SemVer 2.0, rc.10 passe après rc.2 (comparaison numérique des segments séparés par des points).
  • Métadonnées de build : suffixe +sha.abc123, ignoré pour l'ordre des versions.
  • NuGet ajoute un 4ᵉ segment (Revision) et compare les étiquettes de préversion sans tenir compte de la casse. Par défaut, il n'installe pas les préversions (-prerelease, Include prerelease).
  • Exemple dans l'écosystème Azure : les modules Azure Verified Modules imposent SemVer et démarrent en 0.1.0 jusqu'à leur première version stable 1.0.0.

4.2 Calendar Versioning (CalVer)#

La version vient de la date de livraison (par exemple 2026.10.3 ou 2026.10.21.2). Adapté aux applications et outils livrés à un rythme régulier, où la date parle plus que la compatibilité. Microsoft Learn ne décrit pas CalVer en détail : la convention de référence est calver.org (format libre, YYYY.MM.MICRO par exemple), comme semver.org pour SemVer.

SemVer CalVer
Dit si c'est compatible Oui (MAJOR) Non
Dit quand c'est sorti Non Oui
Usage type Bibliothèques, packages, API consommées par d'autres Applications, images, outils internes, systèmes d'exploitation

Réponse d'examen : pour une bibliothèque partagée qui doit signaler les ruptures de compatibilité → SemVer. Pour un produit livré à date fixe dont les consommateurs ne dépendent pas d'une API → CalVer possible.

4.3 La stratégie de dépendances#

  • Fixer la plage acceptée dans chaque projet consommateur, et verrouiller les versions résolues dans un fichier de verrouillage versionné dans Git pour des builds reproductibles.
  • Ne jamais dépendre directement d'un registre public dans un pipeline d'entreprise : passer par le feed + upstream, qui garde une copie et devient le point de contrôle (voir aussi Dependabot au chapitre 07).
  • Monter de version MAJOR = décision explicite (revue, tests), pas une mise à jour automatique.

5. Versionner les pipeline artifacts#

5.1 Le run number dans Azure Pipelines#

  • Valeur par défaut : $(Date:yyyyMMdd).$(Rev:r) (par exemple 20261021.3). $(Rev:r) n'existe que dans le run number et repart à 1 dès qu'une autre partie du numéro change (la date, ou le préfixe de version).
  • Se règle avec name: au niveau du pipeline YAML (pas dans un template ni un stage) ; $(Build.BuildNumber) le relit dans les scripts. Fuseau : UTC. 255 caractères maximum, sans ", /, \, :, <, >, ', |, ?, @, *.
  • $(Rev:rr) ajoute un zéro en tête, mais NuGet et d'autres outils le retirent : à éviter dans un numéro de version.
name: $(majeur).$(mineur).$(Rev:r)          # 1.4.1, 1.4.2… repart à 1 si mineur change

variables:
  majeur: 1
  mineur: 4
  # Alternative : un compteur explicite, indépendant du run number
  patch: $[counter(format('{0}.{1}', variables.majeur, variables.mineur), 0)]
  packageVersion: $(majeur).$(mineur).$(patch)
  • counter(prefix, seed) : incrémente à chaque run, un compteur par valeur de préfixe ; si on revient à un ancien préfixe, il reprend où il en était. Utilisable seulement dans la définition d'une variable, jamais dans une condition. Les compteurs sont propres à un pipeline.
  • Pour les Universal Packages, versionOption: major | minor | patch | custom incrémente à partir de la dernière version du feed.

5.2 Build once, deploy many#

  • On versionne et on construit une seule fois, puis on promeut le même binaire de stage en stage (dev → test → prod) : ce qui a été testé est exactement ce qui part en production.
  • Passage entre stages : publish (raccourci de PublishPipelineArtifact@1) puis download: current. Dans un deployment job, les artifacts sont téléchargés automatiquement dans $(Pipeline.Workspace) (hook deploy) ; download: none l'empêche.
  • Entre pipelines : déclarer un pipeline resource, puis download: <ressource>.
  • PublishPipelineArtifact@1 n'existe que sur Azure DevOps Services ; sur Azure DevOps Server, on utilise Publish Build Artifacts.
- stage: Build
  jobs:
  - job: build
    steps:
    - script: dotnet publish -c Release -o $(Build.ArtifactStagingDirectory)/app
    - publish: $(Build.ArtifactStagingDirectory)/app
      artifact: app-$(Build.BuildNumber)     # le nom porte la version
- stage: Deploy
  dependsOn: Build
  jobs:
  - deployment: deploy
    environment: prod
    strategy:
      runOnce:
        deploy:
          steps:
          - script: ls $(Pipeline.Workspace)   # l'artifact y est déjà téléchargé

Côté GitHub Actions : actions/upload-artifact@v4 (artifacts immuables en v4) avec retention-days, qui ne peut pas dépasser la limite fixée au niveau du dépôt, de l'organisation ou de l'entreprise. La stratégie de rétention complète est au chapitre 06.


6. Quality gates et release gates#

Quality gates et release gates : l'ordre d'évaluation des checks

6.1 Les types de gates#

Où Mécanisme Exemples
Pull request Branch policies / branch protection rules (chapitre 02) build validation, tests obligatoires, reviewers, statut de couverture
Dans le pipeline Une tâche qui échoue tests rouges, seuil de couverture, analyse de sécurité, failureCriteria de charge
Avant un stage (YAML) Approvals and checks sur une ressource approbation, heures ouvrées, alertes Azure Monitor, template obligatoire
Avant / après un stage (Classic) Deployment gates Query work items, Azure Policy compliance
GitHub Actions Environment protection rules required reviewers, wait timer, branches autorisées

6.2 Approvals and checks (Azure Pipelines YAML)#

L'essentiel pour la stratégie de gates (le détail complet, l'ordre et les exemples YAML sont au chapitre 04, § 10.2) :

  • posés hors du YAML par le propriétaire de la ressource protégée (environment, service connection, repository, variable group, secure file, agent pool) : l'auteur du pipeline ne peut pas les contourner ;
  • cinq catégories dans l'ordre : static checks (Branch control, Required template, Evaluate artifact) → pre-check approvals → dynamic checks (Approval, Invoke Azure Function, Invoke REST API, Business Hours, Query Azure Monitor alerts) → post-check approvals → exclusive lock ;
  • gouvernance : Required template impose d'étendre un template approuvé, Branch control réserve la production à main ; bypass en hotfix seulement pour un administrateur de la ressource, et tracé.

6.3 Deployment gates (Classic release)#

  • En pre-deployment et post-deployment conditions d'un stage de release classique.
  • Gates intégrés : Invoke Azure Function, Query Azure Monitor alerts, Invoke REST API, Query work items (nombre de work items dans un seuil, par exemple zéro bug P0), Check Azure Policy compliance.
  • Réglages : Delay before evaluation (laisser le système se stabiliser), timeout (6 minutes minimum) et sampling interval (5 minutes minimum). Les gates sont réévalués jusqu'à ce qu'ils réussissent tous dans le même intervalle.

6.4 GitHub Actions : environment protection rules#

Côté GitHub, la gate avant déploiement est l'environment et ses protection rules : required reviewers (6 au maximum, une approbation suffit), wait timer (jusqu'à 30 jours), deployment branches and tags, custom deployment protection rules (GitHub Apps, preview). Sur un dépôt privé, reviewers et wait timer demandent GitHub Enterprise. Tableau complet et disponibilité par plan : chapitre 04, § 10.5.

6.5 Les gates de sécurité et de gouvernance#

  • Sécurité : faire échouer le build sur une vulnérabilité critique (GitHub Advanced Security, Dependabot, Microsoft Defender for Cloud DevOps security, voir chapitre 07), analyser les images de conteneur avant publication.
  • Gouvernance : Required template, Branch control, Check Azure Policy compliance, approbations des propriétaires de l'environnement de production.
  • Qualité : pourcentage de tests réussis, couverture minimale, critères de charge, pas d'alerte Azure Monitor active après déploiement (post-deployment).

7. Concevoir la stratégie de test#

Shift left : chaque niveau de test à sa place dans le cycle

7.1 La taxonomie de Microsoft (L0 à L4)#

Niveau Nature Dépendances Où le lancer
L0 / L1 Tests unitaires seulement le code testé (L0 : en mémoire) poste, chaque PR
L2 Tests fonctionnels l'assembly + SQL, fichiers… PR ou CI
L3 Fonctionnels contre un service déployé déploiement de test, stubs pour les dépendances clés stage de test
L4 Intégration en production déploiement complet production

Repères chiffrés de Microsoft pour les tests unitaires : moins de 60 ms en moyenne par test L0, moins de 400 ms par test L1, aucun test au-delà de 2 secondes. Une équipe Microsoft lance plus de 60 000 tests unitaires en parallèle en moins de six minutes.

7.2 Les principes#

  • Shift left : faire le maximum de tests avant le merge dans la branche principale ; shift right : garder en production ce qui n'a de sens qu'avec le vrai trafic (smoke tests, surveillance, injection de pannes).
  • Écrire chaque test au niveau le plus bas possible ; privilégier les tests sans dépendance externe.
  • Viser la fiabilité : un test instable coûte cher ; Microsoft décourage les tests UI, souvent instables.
  • Tests fonctionnels isolés : état connu au départ, exécutables dans n'importe quel ordre, uniquement par l'API publique.
  • Le code de test est du code produit : même revue, à côté du code, sous la responsabilité de son propriétaire.
  • Tests locaux : le développeur lance au moins L0/L1 (et souvent L2) avant le commit ; la PR échoue si les tests échouent.

7.3 Les tests de charge avec Azure Load Testing#

  • Service managé qui exécute des scripts Apache JMeter ou Locust, déclenchable depuis Azure Pipelines, GitHub Actions ou l'Azure CLI.
  • failureCriteria dans le fichier YAML de configuration (versionné avec le script) : le test échoue, et le pipeline avec, si un seuil client est dépassé. autoStop arrête le test si le taux d'erreur s'emballe (évite de payer un test cassé).
  • Les critères sur des métriques serveur (CPU d'une App Service…) se configurent dans le portail, pas depuis le pipeline ; la managed identity de la ressource Load Testing doit alors avoir Monitoring Reader sur le composant surveillé.
# loadtest.yaml : versionné à côté de script.jmx
version: v0.1
testId: api-commandes
displayName: Charge sur l'API commandes
testPlan: script.jmx
engineInstances: 1
failureCriteria:
  - avg(response_time_ms) > 300          # moyenne au-delà de 300 ms : échec
  - percentage(error) > 5                # plus de 5 % d'erreurs : échec
  - GetCommande: avg(latency) > 200      # critère sur une requête précise (nom du sampler JMeter)
autoStop:
  errorPercentage: 80
  timeWindow: 120

8. Exécuter les tests dans le pipeline#

Exécuter les tests et intégrer leurs résultats dans le pipeline

8.1 Les tâches de test#

Tâche Usage
DotNetCoreCLI@2 (command: test) tests .NET ; publie seul ses résultats ; prend en charge Microsoft.Testing.Platform
VSTest@3 runner Visual Studio Test (MSTest, xUnit, NUnit, tests Selenium…) ; distribution sur plusieurs agents, rerun, Test Impact Analysis. Ne prend pas en charge Microsoft.Testing.Platform
PublishTestResults@2 publie des résultats produits par n'importe quel runner : formats JUnit (défaut), NUnit, VSTest (TRX), XUnit, CTest
Maven, Gradle, Ant… options intégrées de publication des résultats
- script: npm test -- --reporter mocha-junit-reporter --reporter-options mochaFile=./test-results.xml
- task: PublishTestResults@2
  condition: succeededOrFailed()          # publier même si des tests ont échoué
  inputs:
    testResultsFormat: JUnit
    testResultsFiles: '**/test-results.xml'
    failTaskOnFailedTests: true            # le job échoue sur un test rouge

testResultsFormat: VSTest désigne le format TRX, quel que soit le runner qui l'a produit.

8.2 Les agents de test#

  • Microsoft-hosted : outils standards préinstallés, machine neuve à chaque job, rien à maintenir.
  • Self-hosted : nécessaire pour un réseau privé, des outils spécifiques ou des tests UI sur navigateur physique, qui exigent un agent en mode interactif avec autologon (pas en service). Un navigateur headless n'en a pas besoin.
  • VSTest@3 exige la capacité vstest : Visual Studio installé sur l'agent, ou la tâche Visual Studio Test Platform Installer en amont.
  • L'ancien couple Visual Studio Test Agent Deployment / Run functional tests est déprécié : on utilise VSTest@3.

8.3 Accélérer et fiabiliser#

  • Parallélisme : un job multi-agents (strategy: parallel) et VSTest@3 répartit les tests en lots, selon distributionBatchType : basedOnTestCases (nombre de tests et d'agents), basedOnExecutionTime (durées passées, lots d'égale durée), basedOnAssembly. dontDistribute: true réplique les tests sur chaque agent au lieu de les répartir.
  • Rerun : rerunFailedTests relance les tests échoués, avec un seuil (rerunType) au-delà duquel on ne relance pas.
  • Test Impact Analysis : ne lance que les tests touchés par le changement.
  • Garde-fou : failOnMinTestsNotRun fait échouer la tâche si trop peu de tests ont tourné (adaptateur cassé, filtre erroné).
  • Les tests data-driven (xUnit, NUnit, MSTest) ont des limites : pas de distribution ni de TIA, et le rerun seulement avec des paramètres statiques.
jobs:
- job: tests
  strategy:
    parallel: 3                        # 3 agents se partagent les lots
  pool:
    vmImage: windows-latest
  steps:
  - task: VSTest@3
    inputs:
      testAssemblyVer2: '**\*Tests.dll'
      distributionBatchType: basedOnExecutionTime
      rerunFailedTests: true

8.4 Où lire les résultats#

L'onglet Tests du run (et les analytics de tests) agrège les résultats, regroupe les tentatives d'un rerun et l'historique d'un test, ce qui aide à repérer les tests instables (traités au chapitre 06).

Côté GitHub Actions, le runner produit ses fichiers de résultats ; on les conserve avec actions/upload-artifact@v4 et on fait échouer le job sur un code de sortie non nul du runner de tests.


9. La couverture de code#

  • PublishCodeCoverageResults@2 publie des résumés Cobertura, JaCoCo, Clover, gcov, pcov et .coverage ; plusieurs fichiers de résumé (motif minimatch) sont fusionnés en un seul rapport. La v1, limitée à Cobertura et JaCoCo, sera dépréciée. pathToSources est nécessaire quand le rapport ne contient pas de chemins absolus (cas de JaCoCo), failIfCoverageEmpty fait échouer la tâche s'il n'y a rien à publier.
  • En .NET : le package coverlet.collector et --collect "XPlat Code Coverage" produisent un rapport Cobertura ; VSTest et dotnet test savent aussi produire des fichiers .coverage.
  • Onglet Code Coverage : vue par dossier et par fichier, et rapport HTML selon le format.
  • Statut de couverture en pull request : fichier azurepipelines-coverage.yml à la racine du dépôt, qui fixe un seuil sur la couverture des lignes modifiées (diff coverage). Seul le format .coverage de Visual Studio est accepté pour valider la couverture d'une PR, et elle n'est pas fusionnée entre plusieurs pipelines.
- task: DotNetCoreCLI@2
  inputs:
    command: test
    arguments: '--collect "XPlat Code Coverage"'
- task: PublishCodeCoverageResults@2
  inputs:
    summaryFileLocation: '$(Agent.TempDirectory)/**/coverage.cobertura.xml'
    failIfCoverageEmpty: true
# azurepipelines-coverage.yml (racine du dépôt) : statut de PR sur la couverture des lignes modifiées
coverage:
  status:
    comments: on
    diff:
      target: 70%

Un pourcentage de couverture n'est pas une preuve de qualité : utilisez-le comme garde-fou sur le code modifié plutôt que comme objectif global.


10. Labs conseillés#

À faire dans une organisation Azure DevOps de test (les 2 GiB gratuits d'Azure Artifacts suffisent) et un dépôt GitHub personnel.

Lab 1 · Feed, upstream et views (30 min, gratuit)

  1. Créez un feed scopé projet avec Include packages from common public sources.
  2. Pointez un projet .NET ou Node sur ce seul feed et restaurez : les packages publics apparaissent dans le feed (Source : nuget.org).
  3. Publiez votre propre package 1.0.0, puis promouvez-le vers @prerelease.
  4. Essayez de republier 1.0.0 : refusé (immuabilité).
  5. Résultat attendu : @local contient tout, @prerelease seulement la version promue.

Lab 2 · Publier depuis un pipeline avec le bon rôle (30 min)

  1. Pipeline YAML : NuGetAuthenticate@1, dotnet pack, dotnet nuget push.
  2. Premier run : 403 sur le push, car le Build Service n'est que Collaborator.
  3. Passez le Build Service en Feed Publisher (Contributor), relancez : la publication réussit.
  4. Ajoutez une variable counter pour le PATCH et vérifiez qu'il augmente à chaque run.

Lab 3 · Tests, résultats et couverture (30 min)

  1. Ajoutez un test qui échoue volontairement.
  2. dotnet test avec --collect "XPlat Code Coverage", puis PublishCodeCoverageResults@2.
  3. Vérifiez l'onglet Tests (échec visible) et l'onglet Code Coverage.
  4. Corrigez le test et ajoutez azurepipelines-coverage.yml avec target: 70% ; ouvrez une PR et observez le statut.

Lab 4 · Approvals and checks (20 min)

  1. Créez un environment production avec un check Approval et un check Business Hours.
  2. Ajoutez un stage deployment qui le cible : le run s'arrête avant le stage en attendant.
  3. Ajoutez un Exclusive lock et lancez deux runs : avec runLatest, seul le dernier passe.

Lab 5 · Charge avec Azure Load Testing (40 min, payant à l'usage)

  1. Créez une ressource Azure Load Testing et un test à partir d'une URL.
  2. Ajoutez un loadtest.yaml avec failureCriteria et lancez-le depuis le pipeline (tâche Azure Load Testing).
  3. Mettez un seuil impossible (avg(response_time_ms) > 1) : le pipeline échoue.
  4. Nettoyage : supprimez le groupe de ressources.

11. Pièges d'examen#

Situation dans la question Réponse attendue Pourquoi
Restaurer des packages publics de façon fiable même si nuget.org tombe Upstream sources sur un feed Azure Artifacts Le feed garde une copie de chaque package consommé
Plusieurs sources dans nuget.config, résultats imprévisibles Un seul feed avec upstreams NuGet interroge les sources en parallèle
Publier directement dans @release Impossible : publier dans le feed puis promouvoir Seule @local accepte la publication
Remettre une version promue dans @local Impossible Pas de rétrogradation
Republier 1.2.3 après l'avoir supprimée Impossible : publier 1.2.4 Les versions sont immuables, même supprimées
Garder certaines versions malgré la retention policy Les promouvoir dans une view Les versions promues sont exemptées
Le pipeline restaure mais ne publie pas (403) Build Service en Feed Publisher (Contributor) Il n'est que Collaborator par défaut
Un Reader n'arrive pas à installer un package public jamais utilisé Il faut un Collaborator pour le premier enregistrement Les Readers ne voient que ce qui est déjà sauvegardé
Pipeline d'un projet A, feed du projet B Build Service dans les Readers du projet B + rôle sur le feed Feed scopé projet : double accès
Partager les packages validés avec toute l'organisation sans ouvrir le feed View @release avec visibilité People in my organization Les views se partagent indépendamment
Feed d'une autre organisation du même tenant Locator azure-feed://…@view + view partagée au tenant Seul le propriétaire peut ouvrir la view
Publier un package npm vers GitHub Packages depuis Actions permissions: packages: write + GITHUB_TOKEN Sans permission, le jeton ne publie pas
Bibliothèque qui doit signaler ses ruptures SemVer, incrément du MAJOR CalVer ne dit rien de la compatibilité
Numéro qui repart à zéro quand la version mineure change counter(prefix, seed) ou $(Rev:r) dans le run number Le compteur dépend du préfixe
Utiliser counter() dans une condition de step Impossible Seulement dans la définition d'une variable
Le pipeline doit ignorer un check en urgence Bypass par un administrateur de la ressource Possible pour Approval, Business Hours, Azure Function, REST API
Empêcher un auteur de pipeline de retirer l'approbation de production Mettre le check sur l'environment, pas dans le YAML Les checks ne vivent pas dans le fichier
Imposer un modèle de pipeline sécurisé pour une service connection de prod Check Required template Check statique de gouvernance
Un seul déploiement à la fois sur un environnement Check Exclusive lock (runLatest ou sequential) Évite les déploiements concurrents
Bloquer une release tant qu'il reste un bug P0 (Classic) Gate Query work items avec un seuil maximum à 0 Gate intégré de release
Le rapport de tests manque quand un test échoue condition: succeededOrFailed() sur PublishTestResults@2 Sinon l'étape est sautée
Tests UI Selenium sur un vrai navigateur Agent self-hosted en mode interactif avec autologon Un service n'a pas de session graphique
Réduire la durée d'une grosse suite VSTest Job multi-agents + distributionBatchType: basedOnExecutionTime Lots d'égale durée
Ne lancer que les tests concernés par le changement Test Impact Analysis Sélection automatique
Publier une couverture au format Clover, gcov ou .coverage PublishCodeCoverageResults@2 La v1 ne connaît que Cobertura et JaCoCo
Statut de couverture en PR qui ne marche pas avec Cobertura Utiliser le format .coverage Seul format accepté pour la validation de PR
Faire échouer le pipeline si la latence dépasse 300 ms sous charge failureCriteria d'Azure Load Testing Le statut du test devient celui du pipeline
Critère sur le CPU du serveur depuis le pipeline Impossible : à configurer dans le portail Les critères serveur ne passent pas par le CI/CD

12. Scénarios corrigés#

1. Une équipe .NET dépend de nuget.org. Lors d'une panne du registre, tous les builds ont échoué. La sécurité veut aussi contrôler ce qui entre. Que proposez-vous ?

Un feed Azure Artifacts scopé projet avec l'upstream nuget.org, et un nuget.config qui ne référence que ce feed. Chaque package consommé est sauvegardé dans le feed : les builds continuent pendant une panne, et le feed devient le point de contrôle (rôles, analyse des dépendances).

2. Une bibliothèque interne doit être testée par deux équipes pilotes avant d'être proposée à toute l'entreprise. Comment la diffuser ?

Publier dans le feed (@local), promouvoir vers @prerelease pour les pilotes (source …/feed@prerelease), puis vers @release une fois validée. Donner aux consommateurs l'accès à la view @release seulement.

3. Le pipeline de publication échoue avec une erreur 403 au dotnet nuget push, alors que le restore fonctionne. Cause et correction ?

Le Build Service a par défaut Feed and Upstream Reader (Collaborator) : il restaure mais ne publie pas. Lui donner Feed Publisher (Contributor) sur le feed (et l'accès Readers au projet si le feed est dans un autre projet).

4. Le stockage Azure Artifacts dépasse 2 GiB à cause de milliers de versions CI. On veut garder les versions livrées. Que faire ?

Activer une retention policy (nombre maximum de versions + jours sans téléchargement) et promouvoir les versions livrées dans une view, ce qui les exempte. Penser que la Recycle Bin garde 30 jours et compte encore dans le stockage.

5. Une API publique change le format de ses réponses. Quelle version publier après 2.3.7 ?

3.0.0 : c'est une rupture de compatibilité, donc incrément du MAJOR (et remise à zéro de MINOR et PATCH).

6. Le pipeline doit produire 1.4.0, 1.4.1, 1.4.2… puis repartir à 1.5.0 quand on passe la variable mineure à 5. Comment faire ?

patch: $[counter(format('{0}.{1}', variables.majeur, variables.mineur), 0)] et une version $(majeur).$(mineur).$(patch). Le compteur est propre à chaque préfixe majeur.mineur (ou name: $(majeur).$(mineur).$(Rev:r), qui repart à 1).

7. La production doit exiger l'approbation du responsable d'exploitation, interdire les déploiements le week-end et vérifier qu'aucune alerte n'est active. L'équipe applicative ne doit pas pouvoir le retirer. Que configurez-vous ?

Sur l'environment production : un check Approval, un check Business Hours et un check Query Azure Monitor alerts. Les checks ne sont pas dans le YAML et seul le propriétaire de l'environment les modifie.

8. Deux merges rapides lancent deux déploiements en production en même temps et se marchent dessus. Correction ?

Un check Exclusive lock sur l'environment, avec lockBehavior: runLatest (seul le dernier run déploie) ou sequential si chaque version doit passer.

9. Une suite de 40 000 tests .NET dure 50 minutes. Comment la réduire sans perdre de couverture ?

Job multi-agents (strategy: parallel) avec VSTest@3 et distributionBatchType: basedOnExecutionTime, plus Test Impact Analysis sur les PR ; à plus long terme, déplacer des tests fonctionnels lents vers des tests unitaires L0/L1 (shift left).

10. L'équipe veut bloquer une PR si le nouveau code est couvert à moins de 80 %. Que mettre en place ?

Un fichier azurepipelines-coverage.yml avec diff: target: 80%, un pipeline de build validation qui produit la couverture au format .coverage (VSTest ou dotnet test), et la branch policy qui exige ce statut.


13. Pour aller plus loin#

  • Azure Container Registry pour les images déployées sur Azure (AKS, Container Apps), avec analyse par Defender for Containers : voir le parcours AKS et le cours SC-500.
  • Package graphs d'Azure Artifacts : comment un feed construit les dépendances transitives à partir de ses upstreams.
  • Symbol server d'Azure Artifacts pour déboguer les binaires natifs publiés.
  • Azure Test Plans : tests manuels et exploratoires, et lancement de tests automatisés depuis un plan de test.
  • Azure Chaos Studio avec Azure Load Testing pour la validation continue (approche mission-critical du Well-Architected Framework).

14. Fiche à retenir#

  • GitHub Packages = registres npm, NuGet, Maven, Gradle, RubyGems, conteneurs ; pas d'upstreams ni de views.
  • Azure Artifacts = NuGet, npm, Maven, Gradle, Python, Cargo, Universal Packages ; upstreams, views, retention ; 2 GiB gratuits.
  • Feed scopé projet recommandé ; un seul feed par fichier de configuration client.
  • Ordre de recherche : publiés → déjà sauvegardés → upstreams dans l'ordre ; 20 upstreams max par type.
  • Premier enregistrement depuis un upstream : Collaborator minimum.
  • Publier dans @local, promouvoir ensuite, jamais rétrograder ; une view partagée donne accès sans droit sur le feed.
  • Versions immuables, même après suppression ; retention = nombre max et jours sans téléchargement ; versions promues exemptées ; Recycle Bin 30 jours.
  • Rôles : Reader < Collaborator < Contributor (publie, promeut) < Owner (supprime, retention, upstreams).
  • Build Service = Collaborator par défaut → Contributor pour publier ; feed d'un autre projet = Readers du projet + rôle.
  • NuGetAuthenticate@1 (recommandé) ; permissions: packages: write + GITHUB_TOKEN côté GitHub.
  • SemVer : MAJOR rupture, MINOR fonction, PATCH correctif ; préversion < version finale ; CalVer = date, pas de compatibilité.
  • Run number par défaut $(Date:yyyyMMdd).$(Rev:r) ; counter(prefix, seed) seulement dans une variable.
  • Build once, deploy many ; artifacts téléchargés automatiquement dans un deployment job.
  • Checks dans l'ordre : static → pre-approval → dynamic → post-approval → exclusive lock ; pas dans le YAML ; bypass par l'admin de la ressource.
  • Gates Classic : Azure Function, Azure Monitor alerts, REST API, Query work items, Azure Policy ; timeout ≥ 6 min, échantillonnage ≥ 5 min.
  • GitHub environments : 6 reviewers, wait timer jusqu'à 43 200 min, branches autorisées.
  • Taxonomie L0 à L4, shift left / shift right ; tests unitaires < 60 ms (L0) et < 400 ms (L1).
  • Azure Load Testing : failureCriteria et autoStop dans le YAML ; critères serveur seulement dans le portail.
  • PublishTestResults@2 (JUnit, NUnit, VSTest/TRX, XUnit, CTest) avec succeededOrFailed() ; VSTest@3 multi-agents, rerun, TIA ; UI sur navigateur réel = agent interactif.
  • PublishCodeCoverageResults@2 (Cobertura, JaCoCo, Clover, .coverage… ; fusion des fichiers) ; statut de PR via azurepipelines-coverage.yml, format .coverage seulement.

Références#

Lues le 21/10/2026 :

Sur cette page
Échap

Tape au moins 2 lettres. Plusieurs mots : tous doivent apparaître.