Partager via


Ajouter une extension de tâche de pipelines personnalisée

Azure DevOps Services | Azure DevOps Server | Azure DevOps Server 2022

Ce guide vous guide tout au long de la création, du test et de la publication de tâches de build ou de mise en production personnalisées en tant qu’extensions Azure DevOps. Les tâches de pipeline personnalisées vous permettent d’étendre Azure DevOps avec des fonctionnalités spécialisées adaptées aux flux de travail de votre équipe, des utilitaires simples aux intégrations complexes avec des systèmes externes.

Découvrez comment effectuer les tâches suivantes :

  • Configurer l’environnement de développement et la structure de projet
  • Créer une logique de tâche à l’aide de TypeScript et de la bibliothèque de tâches Azure Pipelines
  • Implémenter des tests unitaires complets avec des frameworks fictifs
  • Empaqueter votre extension pour la distribution
  • Publier sur Visual Studio Marketplace
  • Configurer des pipelines CI/CD automatisés pour la maintenance des extensions

Pour plus d’informations sur Azure Pipelines, consultez Qu’est-ce qu’Azure Pipelines ?

Remarque

Cet article traite des tâches d’agent dans les extensions basées sur l’agent. Pour plus d’informations sur les tâches serveur et les extensions basées sur le serveur, consultez Création de tâches serveur.

Prérequis

Avant de commencer, vérifiez que vous disposez des exigences suivantes :

Composant Besoin Descriptif
Organisation Azure DevOps Obligatoire Créer une organisation si vous n’en avez pas
Éditeur de texte Recommandé Prise en charge de Visual Studio Code pour IntelliSense et du débogage
Node.JS Obligatoire Installez la dernière version (Node.js 20 ou version ultérieure recommandée)
Compilateur TypeScript Obligatoire Installez la dernière version (version 4.6.3 ou ultérieure)
Azure DevOps CLI (tfx-cli) Obligatoire Installer à l’aide des npm i -g tfx-cli extensions de package
Kit de développement logiciel (SDK) d’extension Azure DevOps Obligatoire Installer le package azure-devops-extension-sdk
Framework de test Obligatoire Mocha pour les tests unitaires (installé pendant l’installation)

Structure du projet

Créez un home répertoire pour votre projet. Une fois ce tutoriel terminé, votre extension doit avoir la structure suivante :

|--- README.md    
|--- images                        
    |--- extension-icon.png  
|--- buildandreleasetask            // Task scripts location
    |--- task.json                  // Task definition
    |--- index.ts                   // Main task logic
    |--- package.json               // Node.js dependencies
    |--- tests/                     // Unit tests
        |--- _suite.ts
        |--- success.ts
        |--- failure.ts
|--- vss-extension.json             // Extension manifest

Importante

Votre ordinateur de développement doit exécuter la dernière version de Node.js pour garantir la compatibilité avec l’environnement de production. Mettez à jour votre task.json fichier pour utiliser Node 20 :

"execution": {
    "Node20_1": {
      "target": "index.js"
    }
}

1. Créer une tâche personnalisée

Cette section vous guide tout au long de la création de la structure et de l’implémentation de base de votre tâche personnalisée. Tous les fichiers de cette étape doivent être créés dans le buildandreleasetask dossier du répertoire de home votre projet.

Remarque

Cette procédure pas à pas utilise Windows avec PowerShell. Les étapes fonctionnent sur toutes les plateformes, mais la syntaxe des variables d’environnement diffère. Sur Mac ou Linux, remplacez $env:<var>=<val> par export <var>=<val>.

Configurer la structure des tâches

Créez la structure de projet de base et installez les dépendances requises :

  1. Pour initialiser le projet Node.js, ouvrez PowerShell, accédez à votre buildandreleasetask dossier et exécutez :

    npm init --yes
    

    Le package.json fichier est créé avec les paramètres par défaut. L’indicateur --yes accepte automatiquement toutes les options par défaut.

    Conseil

    Les agents Azure Pipelines s’attendent à ce que les dossiers de tâches incluent des modules de nœud. Copiez node_modules dans votre buildandreleasetask dossier. Pour gérer la taille de fichier VSIX (limite de 50 Mo), envisagez d’exécuter npm install --production ou npm prune --production avant l’empaquetage.

  2. Installez la bibliothèque de tâches Azure Pipelines :

    npm install azure-pipelines-task-lib --save
    
  3. Installez les définitions de type TypeScript :

    npm install @types/node --save-dev
    npm install @types/q --save-dev
    
  4. Configurer des exclusions de contrôle de version

    echo node_modules > .gitignore
    

    Votre processus de génération doit s’exécuter npm install pour reconstruire node_modules chaque fois.

  5. Installez les dépendances de test :

    npm install mocha --save-dev -g
    npm install sync-request --save-dev
    npm install @types/mocha --save-dev
    
  6. Installez le compilateur TypeScript :

    npm install typescript@4.6.3 -g --save-dev
    

    Remarque

    Installez TypeScript globalement pour vous assurer que la tsc commande est disponible. Sans cela, TypeScript 2.3.4 est utilisé par défaut.

  7. Configurer la compilation TypeScript :

    tsc --init --target es2022
    

    Le tsconfig.json fichier est créé avec les paramètres cibles ES2022.

Implémenter la logique de tâche

Une fois la structure terminée, créez les fichiers de tâches principaux qui définissent les fonctionnalités et les métadonnées :

  1. Créez le fichier de définition de tâche : créez task.json dans le buildandreleasetask dossier. Ce fichier décrit votre tâche au système Azure Pipelines, en définissant les entrées, les paramètres d’exécution et la présentation de l’interface utilisateur.

    {
     "$schema": "https://raw.githubusercontent.com/Microsoft/azure-pipelines-task-lib/master/tasks.schema.json",
     "id": "{{taskguid}}",
     "name": "{{taskname}}",
     "friendlyName": "{{taskfriendlyname}}",
     "description": "{{taskdescription}}",
     "helpMarkDown": "",
     "category": "Utility",
     "author": "{{taskauthor}}",
     "version": {
         "Major": 0,
         "Minor": 1,
         "Patch": 0
     },
     "instanceNameFormat": "Echo $(samplestring)",
     "inputs": [
         {
             "name": "samplestring",
             "type": "string",
             "label": "Sample String",
             "defaultValue": "",
             "required": true,
             "helpMarkDown": "A sample string"
         }
     ],
     "execution": {
         "Node20_1": {
             "target": "index.js"
         }
     }
     }
    

    Remarque

    Remplacez {{placeholders}} par les informations réelles de votre tâche. L’taskguid doit être unique. Générez-en une à l’aide de PowerShell : (New-Guid).Guid

  2. Pour implémenter la logique de tâche, créez index.ts avec la fonctionnalité principale de votre tâche :

    import tl = require('azure-pipelines-task-lib/task');
    
     async function run() {
         try {
             const inputString: string | undefined = tl.getInput('samplestring', true);
             if (inputString == 'bad') {
                 tl.setResult(tl.TaskResult.Failed, 'Bad input was given');
                 return;
             }
             console.log('Hello', inputString);
         }
         catch (err: any) {
             tl.setResult(tl.TaskResult.Failed, err.message);
         }
     }
    
     run();
    
  3. Compilez TypeScript en JavaScript :

    tsc
    

    Le index.js fichier est créé à partir de votre source TypeScript.

Présentation des composants task.json

Le task.json fichier est le cœur de votre définition de tâche. Voici les propriétés clés :

Propriété Descriptif Exemple :
id Identificateur GUID unique pour votre tâche Généré à l’aide de (New-Guid).Guid
name Nom de la tâche sans espaces (utilisé en interne) MyCustomTask
friendlyName Nom complet affiché dans l’interface utilisateur My Custom Task
description Description détaillée des fonctionnalités de tâche Performs custom operations on files
author Nom de l’éditeur ou de l’auteur My Company
instanceNameFormat Affichage de la tâche dans les étapes du pipeline Process $(inputFile)
inputs Tableau de paramètres d’entrée Consultez les types d’entrée suivants
execution Spécification de l’environnement d’exécution Node20_1, , PowerShell3etc.
restrictions Restrictions de sécurité pour les commandes et les variables Recommandé pour les nouvelles tâches

Restrictions de sécurité

Pour les tâches de production, ajoutez des restrictions de sécurité pour limiter l’utilisation des commandes et l’accès aux variables :

"restrictions": {
  "commands": {
    "mode": "restricted"
  },
  "settableVariables": {
    "allowed": ["variable1", "test*"]
  }
}

Le mode restreint autorise uniquement ces commandes :

  • logdetail, logissuecomplete, setprogress
  • setsecret, setvariabledebug, settaskvariable
  • prependpath, publish

Variable allowlist contrôle quelles variables peuvent être définies via setvariable ou prependpath. Prend en charge les modèles d’expression régulière de base.

Remarque

Cette fonctionnalité nécessite l’agent version 2.182.1 ou ultérieure.

Types d’entrée et exemples

Types d’entrée courants pour les paramètres de tâche :

"inputs": [
    {
        "name": "stringInput",
        "type": "string",
        "label": "Text Input",
        "defaultValue": "",
        "required": true,
        "helpMarkDown": "Enter a text value"
    },
    {
        "name": "boolInput",
        "type": "boolean",
        "label": "Enable Feature",
        "defaultValue": "false",
        "required": false
    },
    {
        "name": "picklistInput",
        "type": "pickList",
        "label": "Select Option",
        "options": {
            "option1": "First Option",
            "option2": "Second Option"
        },
        "defaultValue": "option1"
    },
    {
        "name": "fileInput",
        "type": "filePath",
        "label": "Input File",
        "required": true,
        "helpMarkDown": "Path to the input file"
    }
]

Tester votre tâche localement

Avant d’empaqueter, testez votre tâche pour vous assurer qu’elle fonctionne correctement :

  1. Test avec une entrée manquante (doit échouer) :

    node index.js
    

    Sortie attendue :

    ##vso[task.debug]agent.workFolder=undefined
    ##vso[task.debug]loading inputs and endpoints
    ##vso[task.debug]loaded 0
    ##vso[task.debug]task result: Failed
    ##vso[task.issue type=error;]Input required: samplestring
    ##vso[task.complete result=Failed;]Input required: samplestring
    
  2. Test avec entrée valide (doit réussir) :

    $env:INPUT_SAMPLESTRING="World"
    node index.js
    

    Sortie attendue :

    ##vso[task.debug]agent.workFolder=undefined
    ##vso[task.debug]loading inputs and endpoints
    ##vso[task.debug]loading INPUT_SAMPLESTRING
    ##vso[task.debug]loaded 1
    ##vso[task.debug]samplestring=World
    Hello World
    
  3. Gestion des erreurs de test :

    $env:INPUT_SAMPLESTRING="bad"
    node index.js
    

    Cette action doit déclencher le chemin de gestion des erreurs dans votre code.

    Conseil

    Pour plus d’informations sur les exécuteurs de tâches et les versions Node.js, consultez les instructions de mise à jour de l’exécuteur node.

Pour plus d’informations, consultez la référence de tâche de compilation/déploiement.

2. Implémenter des tests unitaires complets

Le test de votre tâche garantit soigneusement la fiabilité et aide à détecter les problèmes avant le déploiement vers des pipelines de production.

Installer les dépendances de test

Installez les outils de test requis :

npm install mocha --save-dev -g
npm install sync-request --save-dev
npm install @types/mocha --save-dev

Créer un test

  1. Créez un tests dossier dans votre répertoire de tâches contenant un _suite.ts fichier :

     import * as path from 'path';
     import * as assert from 'assert';
     import * as ttm from 'azure-pipelines-task-lib/mock-test';
    
     describe('Sample task tests', function () {
    
         before( function() {
             // Setup before tests
         });
    
         after(() => {
             // Cleanup after tests
         });
    
         it('should succeed with simple inputs', function(done: Mocha.Done) {
             // Success test implementation
         });
    
         it('should fail if tool returns 1', function(done: Mocha.Done) {
             // Failure test implementation
         });    
       });
    

    Conseil

    Votre dossier de test doit se trouver dans le dossier des tâches (par exemple). buildandreleasetask Si vous rencontrez une erreur de demande de synchronisation, installez-la dans le dossier de tâches : npm i --save-dev sync-request.

  2. Créez success.ts dans votre répertoire de test pour simuler une exécution réussie de la tâche :

     import ma = require('azure-pipelines-task-lib/mock-answer');
     import tmrm = require('azure-pipelines-task-lib/mock-run');
     import path = require('path');
    
     let taskPath = path.join(__dirname, '..', 'index.js');
     let tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);
    
     // Set valid input for success scenario
     tmr.setInput('samplestring', 'human');
    
     tmr.run();
    
  3. Ajoutez le test de réussite à votre _suite.ts fichier :

     it('should succeed with simple inputs', function(done: Mocha.Done) {
         this.timeout(1000);
    
         let tp: string = path.join(__dirname, 'success.js');
         let tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
    
         tr.runAsync().then(() => {
             console.log(tr.succeeded);
             assert.equal(tr.succeeded, true, 'should have succeeded');
             assert.equal(tr.warningIssues.length, 0, "should have no warnings");
             assert.equal(tr.errorIssues.length, 0, "should have no errors");
             console.log(tr.stdout);
             assert.equal(tr.stdout.indexOf('Hello human') >= 0, true, "should display Hello human");
             done();
         }).catch((error) => {
             done(error); // Ensure the test case fails if there's an error
         });
     });
    
  4. Créez failure.ts dans votre répertoire de test pour tester la gestion des erreurs :

    import ma = require('azure-pipelines-task-lib/mock-answer');
    import tmrm = require('azure-pipelines-task-lib/mock-run');
    import path = require('path');
    
    let taskPath = path.join(__dirname, '..', 'index.js');
    let tmr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);
    
    // Set invalid input to trigger failure
    tmr.setInput('samplestring', 'bad');
    
    tmr.run();
    
  5. Ajoutez le test d’échec à votre _suite.ts fichier :

     it('should fail if tool returns 1', function(done: Mocha.Done) {
         this.timeout(1000);
    
         const tp = path.join(__dirname, 'failure.js');
         const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
    
         tr.runAsync().then(() => {
             console.log(tr.succeeded);
             assert.equal(tr.succeeded, false, 'should have failed');
             assert.equal(tr.warningIssues.length, 0, 'should have no warnings');
             assert.equal(tr.errorIssues.length, 1, 'should have 1 error issue');
             assert.equal(tr.errorIssues[0], 'Bad input was given', 'error issue output');
             assert.equal(tr.stdout.indexOf('Hello bad'), -1, 'Should not display Hello bad');
             done();
         });
     });
    

Exécuter vos tests

Exécutez la suite de tests :

# Compile TypeScript
tsc

# Run tests
mocha tests/_suite.js

Les deux tests doivent réussir. Pour une sortie détaillée (similaire à la sortie de la console de génération), définissez la variable d’environnement de trace :

$env:TASK_TEST_TRACE=1
mocha tests/_suite.js

Meilleures pratiques relatives à la couverture des tests

  • Tester toutes les combinaisons d’entrées : entrées valides, entrées non valides, entrées manquantes requises
  • Scénarios d’erreur de test : échecs réseau, erreurs du système de fichiers, problèmes d’autorisation
  • Simuler des dépendances externes : ne pas compter sur les services externes dans les tests unitaires
  • Valider les sorties : vérifier la sortie de la console, les résultats des tâches et les artefacts générés
  • Tests de performances : envisagez d’ajouter des tests pour les tâches qui traitent des fichiers volumineux

Bonnes pratiques de sécurité

  • Validation d’entrée : Toujours valider et nettoyer les entrées
  • Gestion des secrets : utilisation setSecret pour les données sensibles
  • Restrictions de commande : Implémenter des restrictions de commande pour les tâches de production
  • Autorisations minimales : Demander uniquement les autorisations nécessaires
  • Mises à jour régulières : Conserver les dépendances et les versions Node.js actuelles

Après avoir testé votre tâche localement et implémenté des tests unitaires complets, empaquetez-la dans une extension pour Azure DevOps.

Installer des outils d’empaquetage

Installez l’interface de ligne de commande multiplateforme (tfx-cli) :

npm install -g tfx-cli

Créer le manifeste d’extension

Le manifeste d’extension (vss-extension.json) contient toutes les informations relatives à votre extension, y compris les références à vos dossiers de tâches et images.

  1. Créer un dossier d’images avec un extension-icon.png fichier

  2. Créez vss-extension.json dans le répertoire racine de votre extension (et non dans le dossier de tâches) :

    {
     "manifestVersion": 1,
     "id": "my-custom-tasks",
     "name": "My Custom Tasks",
     "version": "1.0.0",
     "publisher": "your-publisher-id",
     "targets": [
         {
             "id": "Microsoft.VisualStudio.Services"
         }
     ],
     "description": "Custom build and release tasks for Azure DevOps",
     "categories": [
         "Azure Pipelines"
     ],
     "icons": {
         "default": "images/extension-icon.png"
     },
     "files": [
         {
             "path": "MyCustomTask"
         }
     ],
     "contributions": [
         {
             "id": "my-custom-task",
             "type": "ms.vss-distributed-task.task",
             "targets": [
                 "ms.vss-distributed-task.tasks"
             ],
             "properties": {
                 "name": "MyCustomTask"
             }
         }
     ]
    }
    

Propriétés du manifeste de clé

Propriété Descriptif
publisher Identificateur de votre éditeur de la Place de marché
contributions.id Identificateur unique dans l’extension
contributions.properties.name Doit correspondre au nom de votre dossier de tâche
files.path Chemin d’accès à votre dossier de tâches par rapport au manifeste

Remarque

Remplacez la valeur de l’éditeur par le nom de votre éditeur. Pour plus d’informations sur la création d’un éditeur, consultez Créer votre éditeur.

Emballer votre extension

Empaqueter votre extension dans un fichier .vsix :

tfx extension create --manifest-globs vss-extension.json

Gestion des versions

  • Version de l’extension : incrémenter la version vss-extension.json pour chaque mise à jour
  • Version de la tâche : incrémenter la version pour task.json chaque mise à jour de tâche
  • Incrément automatique : permet --rev-version d’incrémenter automatiquement la version du correctif
tfx extension create --manifest-globs vss-extension.json --rev-version

Importante

La version de tâche et la version d’extension doivent être mises à jour pour que les modifications prennent effet dans Azure DevOps.

Stratégie de contrôle de version

Suivez les principes de gestion de version sémantique pour les mises à jour de vos tâches :

  • Version majeure : changements cassants des entrées/sorties
  • Version mineure : nouvelles fonctionnalités, rétrocompatibles
  • Version du correctif : correctifs de bogues uniquement

Processus de mise à jour :

  1. Mise à jour de la version task.json
  2. Mise à jour de la version vss-extension.json
  3. Tester soigneusement dans une organisation de test
  4. Publier et surveiller les problèmes

Publier sur Visual Studio Marketplace

1. Créer votre éditeur

  1. Connectez-vous au portail de publication visual Studio Marketplace
  2. Créez un serveur de publication si vous y êtes invité :
    • Identificateur de l’éditeur : utilisé dans votre manifeste d’extension (par exemple, mycompany-myteam)
    • Nom complet : Nom public affiché dans la Place de marché (par exemple, My Team)
  3. Passer en revue et accepter le contrat d’éditeur de la Place de marché

2. Charger votre extension

Méthode d’interface web :

  1. Sélectionner Charger une nouvelle extension
  2. Choisir votre fichier empaqueté .vsix
  3. Sélectionnez Charger.

Méthode de ligne de commande :

tfx extension publish --manifest-globs vss-extension.json --share-with yourOrganization

3. Partager votre extension

  1. Cliquez avec le bouton droit sur votre extension sur la Place de marché
  2. Sélectionnez Partager
  3. Entrez le nom de votre organisation
  4. Ajouter d’autres organisations en fonction des besoins

Importante

Les éditeurs doivent être vérifiés pour partager des extensions publiquement. Pour plus d’informations, consultez Package/Publish/Install.

4. Installer dans votre organisation

Après le partage, installez l’extension dans votre organisation Azure DevOps :

  1. Accéder auxextensions> de l’organisation
  2. Rechercher votre extension
  3. Sélectionnez Obtenir gratuitement et installer

3. Empaqueter et publier votre extension

Vérifier votre extension

Après l’installation, vérifiez que votre tâche fonctionne correctement :

  1. Créez ou modifiez un pipeline.
  2. Ajoutez votre tâche personnalisée :
    • Sélectionner Ajouter une tâche dans l’éditeur de pipeline
    • Rechercher votre tâche personnalisée par nom
    • Ajoutez-le à votre pipeline
  3. Configurer les paramètres de tâche :
    • Définir les entrées requises
    • Configurer les paramètres facultatifs
  4. Exécuter le pipeline pour tester la fonctionnalité
  5. Surveiller l’exécution :
    • Vérifier les journaux des tâches pour une exécution appropriée
    • Vérifier les sorties attendues
    • Vérifiez qu’aucune erreur ou avertissement n’est en cours

4. Automatiser la publication d’extension avec CI/CD

Pour gérer efficacement votre tâche personnalisée, créez des pipelines de génération et de mise en production automatisés qui gèrent les tests, l’empaquetage et la publication.

Conditions préalables pour l’automatisation

  • Tâches d’extension Azure DevOps : installer l’extension gratuitement
  • Groupe de variables : Créez un groupe de variables de bibliothèque de pipelines avec ces variables :
    • publisherId: ID de l’éditeur de la Place de marché
    • extensionId: ID d’extension de vss-extension.json
    • extensionName: Nom de l’extension de vss-extension.json
    • artifactName: Nom de l’artefact VSIX
  • Connexion de service : créer une connexion de service de la Place de marché avec des autorisations d’accès au pipeline

Pipeline CI/CD complet

Créez un pipeline YAML avec des étapes complètes pour le test, l’empaquetage et la publication :

trigger: 
- main

pool:
  vmImage: "ubuntu-latest"

variables:
  - group: extension-variables # Your variable group name

stages:
  - stage: Test_and_validate
    displayName: 'Run Tests and Validate Code'
    jobs:
      - job: RunTests
        displayName: 'Execute unit tests'
        steps:
          - task: TfxInstaller@4
            displayName: 'Install TFX CLI'
            inputs:
              version: "v0.x"
          
          - task: Npm@1
            displayName: 'Install task dependencies'
            inputs:
              command: 'install'
              workingDir: '/MyCustomTask' # Update to your task directory
          
          - task: Bash@3
            displayName: 'Compile TypeScript'
            inputs:
              targetType: "inline"
              script: |
                cd MyCustomTask # Update to your task directory
                tsc
          
          - task: Npm@1
            displayName: 'Run unit tests'
            inputs:
              command: 'custom'
              workingDir: '/MyCustomTask' # Update to your task directory
              customCommand: 'test' # Ensure this script exists in package.json
          
          - task: PublishTestResults@2
            displayName: 'Publish test results'
            inputs:
              testResultsFormat: 'JUnit'
              testResultsFiles: '**/test-results.xml'
              searchFolder: '$(System.DefaultWorkingDirectory)'

  - stage: Package_extension
    displayName: 'Package Extension'
    dependsOn: Test_and_validate
    condition: succeeded()
    jobs:
      - job: PackageExtension
        displayName: 'Create VSIX package'
        steps:
          - task: TfxInstaller@4
            displayName: 'Install TFX CLI'
            inputs:
              version: "v0.x"
          
          - task: Npm@1
            displayName: 'Install dependencies'
            inputs:
              command: 'install'
              workingDir: '/MyCustomTask'
          
          - task: Bash@3
            displayName: 'Compile TypeScript'
            inputs:
              targetType: "inline"
              script: |
                cd MyCustomTask
                tsc
          
          - task: QueryAzureDevOpsExtensionVersion@4
            name: QueryVersion
            displayName: 'Query current extension version'
            inputs:
              connectTo: 'VsTeam'
              connectedServiceName: 'marketplace-connection'
              publisherId: '$(publisherId)'
              extensionId: '$(extensionId)'
              versionAction: 'Patch'
          
          - task: PackageAzureDevOpsExtension@4
            displayName: 'Package extension'
            inputs:
              rootFolder: '$(System.DefaultWorkingDirectory)'
              publisherId: '$(publisherId)'
              extensionId: '$(extensionId)'
              extensionName: '$(extensionName)'
              extensionVersion: '$(QueryVersion.Extension.Version)'
              updateTasksVersion: true
              updateTasksVersionType: 'patch'
              extensionVisibility: 'private'
              extensionPricing: 'free'
          
          - task: PublishBuildArtifacts@1
            displayName: 'Publish VSIX artifact'
            inputs:
              PathtoPublish: '$(System.DefaultWorkingDirectory)/*.vsix'
              ArtifactName: '$(artifactName)'
              publishLocation: 'Container'

  - stage: Publish_to_marketplace
    displayName: 'Publish to Marketplace'
    dependsOn: Package_extension
    condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
    jobs:
      - deployment: PublishExtension
        displayName: 'Deploy to marketplace'
        environment: 'marketplace-production'
        strategy:
          runOnce:
            deploy:
              steps:
                - task: TfxInstaller@4
                  displayName: 'Install TFX CLI'
                  inputs:
                    version: "v0.x"
                
                - task: PublishAzureDevOpsExtension@4
                  displayName: 'Publish to marketplace'
                  inputs:
                    connectTo: 'VsTeam'
                    connectedServiceName: 'marketplace-connection'
                    fileType: 'vsix'
                    vsixFile: '$(Pipeline.Workspace)/$(artifactName)/*.vsix'
                    publisherId: '$(publisherId)'
                    extensionId: '$(extensionId)'
                    extensionName: '$(extensionName)'
                    updateTasksVersion: false
                    extensionVisibility: 'private'
                    extensionPricing: 'free'

Configurer package.json pour les tests

Ajoutez des scripts de test à votre package.json:

{
  "scripts": {
    "test": "mocha tests/_suite.js --reporter xunit --reporter-option output=test-results.xml",
    "test-verbose": "cross-env TASK_TEST_TRACE=1 npm test"
  }
}

Répartition des phases de pipeline

Étape 1 : Tester et valider

  • Objectif : garantir la qualité et les fonctionnalités du code
  • Actions : Installer des dépendances, compiler TypeScript, exécuter des tests unitaires, publier des résultats
  • Validation : tous les tests doivent réussir pour continuer

Étape 2 : Extension de package

  • Objectif : Créer un package VSIX déployable
  • Actions : Interroger la version actuelle, incrémenter la version, l’extension de package, publier des artefacts
  • Contrôle de version : gère automatiquement les incréments de version

Étape 3 : Publier sur la Place de marché

  • Objectif : Déployer sur Visual Studio Marketplace
  • Conditions : s’exécute uniquement sur la branche principale après la réussite de l’empaquetage
  • Environnement : utilise l’environnement de déploiement pour les portes d’approbation

Meilleures pratiques pour CI/CD

  • Protection des branches : publier uniquement à partir des branches main/release
  • Portes d’environnement : utiliser des environnements de déploiement pour les versions de production
  • Gestion des versions : Automatiser les incréments de version pour éviter les conflits
  • Couverture des tests : garantir une couverture complète des tests avant l’empaquetage
  • Sécurité : Utiliser des connexions de service au lieu d’informations d’identification codées en dur
  • Surveillance : configurer des alertes pour les déploiements ayant échoué

Pour les pipelines de build classiques, procédez comme suit pour configurer l’empaquetage et la publication d’extensions :

  1. Ajoutez la Bash tâche pour compiler le TypeScript en JavaScript.

  2. Pour interroger la version existante, ajoutez la tâche version de l’extension de requête à l’aide des entrées suivantes :

    • Se connecter à : Visual Studio Marketplace
    • Visual Studio Marketplace (connexion de service) : connexion de service
    • ID de l’éditeur : ID de votre éditeur visual Studio Marketplace
    • ID d’extension : ID de votre extension dans le vss-extension.json fichier
    • Augmenter la version : Correctif
    • Variable de sortie : Task.Extension.Version
  3. Pour empaqueter les extensions en fonction du manifeste Json, ajoutez la tâche d’extension de package à l’aide des entrées suivantes :

    • Dossier racine des manifestes : fait référence au répertoire racine qui contient le fichier manifeste. Par exemple, $(System.DefaultWorkingDirectory) est le répertoire racine
    • Fichier manifeste : vss-extension.json
    • ID de l’éditeur : ID de votre éditeur visual Studio Marketplace
    • ID d’extension : ID de votre extension dans le vss-extension.json fichier
    • Nom de l’extension : nom de votre extension dans le vss-extension.json fichier
    • Version de l’extension : $(Task.Extension.Version)
    • Remplacer la version des tâches : cochée (true)
    • Type de remplacement : remplacer uniquement le correctif (1.0.r)
    • Visibilité de l’extension : si l’extension est toujours en cours de développement, définissez la valeur sur privée. Pour rendre l'extension publique, définissez la valeur à public.
  4. Pour copier des fichiers publiés, ajoutez la tâche Copier des fichiers à l’aide des entrées suivantes :

    • Contenu : tous les fichiers à copier pour les publier en tant qu’artefact
    • Dossier cible : dossier dans lequel les fichiers sont copiés
      • Par exemple : $(Build.ArtifactStagingDirectory)
  5. Ajoutez Publier les artefacts de build pour qu'ils puissent être utilisés dans d'autres tâches ou pipelines. Utilisez les entrées suivantes :

    • Chemin d’accès à la publication : chemin d’accès au dossier qui contient les fichiers en cours de publication
      • Par exemple : $(Build.ArtifactStagingDirectory)
    • Nom de l’artefact : nom donné à l’artefact
    • Emplacement de publication des artefacts : choisissez Azure Pipelines pour utiliser les artefacts dans les travaux futurs.

Étape 3 : Télécharger les artefacts de build et publier l’extension

  1. Pour installer tfx-cli sur votre agent de build, ajoutez Use Node CLI for Azure DevOps (tfx-cli).

  2. Pour télécharger les artefacts vers une nouvelle tâche, veuillez ajouter la tâche Télécharger les artefacts de build en utilisant les informations suivantes :

    • Télécharger les artefacts produits par : si vous téléchargez l'artefact sur une nouvelle tâche à partir du même pipeline, sélectionnez Compilation actuelle. Si vous effectuez le téléchargement sur un nouveau pipeline, veuillez sélectionner « Version spécifique ».
    • Type de téléchargement : choisissez un artefact spécifique pour télécharger tous les fichiers publiés.
    • Nom de l’artefact : nom de l’artefact publié
    • Répertoire de destination : dossier dans lequel les fichiers doivent être téléchargés
  3. Pour obtenir la tâche Publier l’extension , utilisez les entrées suivantes :

    • Se connecter à : Visual Studio Marketplace
    • Connexion Visual Studio Marketplace : ServiceConnection
    • Type de fichier d’entrée : fichier VSIX
    • Fichier VSIX : /Publisher.*.vsix
    • ID de l’éditeur : ID de votre éditeur visual Studio Marketplace
    • ID d’extension : ID de votre extension dans le vss-extension.json fichier
    • Nom de l’extension : nom de votre extension dans le vss-extension.json fichier
    • Visibilité de l’extension : privé ou public

Facultatif : Installer et tester votre extension

Après avoir publié votre extension, elle doit être installée dans les organisations Azure DevOps.

Installer l’extension à l’organisation

Installez votre extension partagée en quelques étapes :

  1. Accédez aux paramètres de l’organisation et sélectionnez Extensions.

  2. Recherchez votre extension dans la section Extensions partagées avec moi :

    • Sélectionner le lien d’extension
    • Sélectionnez Obtenir gratuitement ou installer
  3. Vérifiez que l’extension apparaît dans la liste de vos extensions installées :

    • Vérifiez qu’elle est disponible dans votre bibliothèque de tâches de pipeline

Remarque

Si vous ne voyez pas l’onglet Extensions , vérifiez que vous êtes au niveau de l’administration de l’organisation (https://dev.azure.com/{organization}/_admin) et non au niveau du projet.

Tests de bout en bout

Après l’installation, effectuez des tests complets :

  1. Créez un pipeline de test :

    • Ajouter votre tâche personnalisée à un nouveau pipeline
    • Configurer tous les paramètres d’entrée
    • Tester avec différentes combinaisons d’entrée
  2. Valider les fonctionnalités :

    • Exécuter le pipeline et surveiller l’exécution
    • Vérifier les sorties et les journaux des tâches
    • Vérifier la gestion des erreurs avec des entrées non valides
  3. Performances des tests :

    • Tester avec des fichiers d’entrée volumineux (le cas échéant)
    • Surveiller l’utilisation des ressources
    • Valider le comportement du délai d’expiration

Questions fréquentes

Q : Comment l’annulation des tâches est-elle gérée ?

R : L’agent de pipeline envoie et SIGINT signale SIGTERM aux processus de tâche. Bien que la bibliothèque de tâches ne fournisse pas de gestion d’annulation explicite, votre tâche peut implémenter des gestionnaires de signal. Pour plus d’informations, consultez l’annulation des travaux de l’agent.

Q : Comment puis-je supprimer une tâche de mon organisation ?

R : La suppression automatique n’est pas prise en charge , car elle interrompt les pipelines existants. Au lieu de:

  1. Déprécier la tâche : marquer la tâche comme déconseillée
  2. Gestion des versions : Faire passer la version de la tâche
  3. Communication : informer les utilisateurs de la chronologie de dépréciation

Q : Comment puis-je mettre à niveau ma tâche vers la dernière version Node.js ?

R : Effectuez une mise à niveau vers la dernière version de Node pour améliorer les performances et la sécurité. Pour obtenir des conseils sur la migration, consultez Mise à niveau des tâches vers le nœud 20.

Prendre en charge plusieurs versions de Nœud en incluant plusieurs sections d’exécution dans task.json:

"execution": {
  "Node20_1": {
    "target": "index.js"
  },
  "Node10": {
    "target": "index.js"
  }
}

Les agents avec Node 20 utilisent la version préférée, tandis que les anciens agents reviennent à Node 10.

Pour mettre à niveau vos tâches :

  • Pour vous assurer que votre code se comporte comme prévu, testez vos tâches sur les différentes versions de Node Runner.

  • Dans la section d’exécution de votre tâche, mettez à jour depuis Node ou Node10 vers Node16 ou Node20.

  • Pour prendre en charge les versions antérieures du serveur, vous devez laisser la Node/Node10 cible. Les versions antérieures d’Azure DevOps Server peuvent ne pas avoir la dernière version de Node Runner incluse.

  • Vous pouvez choisir de partager le point d’entrée défini dans la cible ou d’avoir des cibles optimisées pour la version du nœud utilisée.

    "execution": {
       "Node10": {
         "target": "bash10.js",
         "argumentFormat": ""
       },
       "Node16": {
         "target": "bash16.js",
         "argumentFormat": ""
       },
       "Node20_1": {
         "target": "bash20.js",
         "argumentFormat": ""
       }
    }
    

Importante

Si vous n’ajoutez pas la prise en charge de l’exécuteur Node 20 à vos tâches personnalisées, elles échouent sur les agents installés à partir du flux de mise en production pipelines-agent-*.