Compartir a través de


Adición de una extensión de tarea de canalizaciones personalizadas

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

En esta guía se explica cómo crear, probar y publicar tareas personalizadas de compilación o versión como extensiones de Azure DevOps. Las tareas de canalización personalizadas permiten ampliar Azure DevOps con funcionalidad especializada adaptada a los flujos de trabajo de su equipo, desde utilidades sencillas hasta integraciones complejas con sistemas externos.

Obtenga información sobre cómo realizar las siguientes tareas:

  • Configuración del entorno de desarrollo y la estructura del proyecto
  • Creación de lógica de tareas mediante TypeScript y la biblioteca de tareas de Azure Pipelines
  • Implementación de pruebas unitarias completas con marcos ficticios
  • Empaquetar la extensión para la distribución
  • Publicación en Visual Studio Marketplace
  • Configuración de canalizaciones automatizadas de CI/CD para el mantenimiento de extensiones

Para más información sobre Azure Pipelines, consulte ¿Qué es Azure Pipelines?

Nota:

En este artículo se tratan las tareas del agente en extensiones basadas en agente. Para obtener información sobre las tareas de servidor y las extensiones basadas en servidor, vea Creación de tareas de servidor.

Requisitos previos

Antes de comenzar, asegúrese de que tiene los siguientes requisitos:

Componente Requisito Descripción
Organización de Azure DevOps Obligatorio Cree una organización si no tiene una
Editor de texto Recomendado Compatibilidad con Visual Studio Code para IntelliSense y depuración
Node.js Obligatorio Instale la versión más reciente (se recomiendaNode.js 20 o posterior)
Compilador de TypeScript Obligatorio Instale la versión más reciente (versión 4.6.3 o posterior)
CLI de Azure DevOps (tfx-cli) Obligatorio Instalación mediante npm i -g tfx-cli para empaquetar extensiones
SDK de extensión de Azure DevOps Obligatorio Instalación del paquete azure-devops-extension-sdk
Marco de pruebas Obligatorio Mocha para pruebas unitarias (instaladas durante la instalación)

Estructura de proyecto

Cree un home directorio para el proyecto. Después de completar este tutorial, la extensión debe tener la estructura siguiente:

|--- 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

La máquina de desarrollo debe ejecutar la versión más reciente de Node.js para garantizar la compatibilidad con el entorno de producción. Actualice el task.json archivo para usar Node 20:

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

1. Crear una tarea personalizada

Esta sección le guía a través de la creación de la estructura básica y la implementación de la tarea personalizada. Todos los archivos de este paso se deben crear dentro de la buildandreleasetask carpeta dentro del directorio del home proyecto.

Nota:

En este tutorial se usa Windows con PowerShell. Los pasos funcionan en todas las plataformas, pero la sintaxis de las variables de entorno difiere. En Mac o Linux, reemplace por $env:<var>=<val>export <var>=<val>.

Configuración del scaffolding de tareas

Cree la estructura básica del proyecto e instale las dependencias necesarias:

  1. Para inicializar el proyecto de Node.js, abra PowerShell, vaya a la buildandreleasetask carpeta y ejecute:

    npm init --yes
    

    El package.json archivo se crea con la configuración predeterminada. La --yes marca acepta automáticamente todas las opciones predeterminadas.

    Sugerencia

    Los agentes de Azure Pipelines esperan que las carpetas de tareas incluyan módulos de nodo. Copie node_modules en la buildandreleasetask carpeta. Para administrar el tamaño del archivo VSIX (límite de 50 MB), considere la posibilidad de ejecutar npm install --production o npm prune --production antes del empaquetado.

  2. Instale la biblioteca de tareas de Azure Pipelines:

    npm install azure-pipelines-task-lib --save
    
  3. Instale definiciones de tipos de TypeScript:

    npm install @types/node --save-dev
    npm install @types/q --save-dev
    
  4. Configuración de exclusiones de control de versiones

    echo node_modules > .gitignore
    

    El proceso de compilación debe ejecutarse para recompilar npm install node_modules cada vez.

  5. Instalar dependencias de prueba:

    npm install mocha --save-dev -g
    npm install sync-request --save-dev
    npm install @types/mocha --save-dev
    
  6. Instale el compilador de TypeScript:

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

    Nota:

    Instale TypeScript globalmente para asegurarse de que el tsc comando está disponible. Sin él, TypeScript 2.3.4 se usa de forma predeterminada.

  7. Configurar la compilación de TypeScript:

    tsc --init --target es2022
    

    El tsconfig.json archivo se crea con la configuración de destino ES2022.

Implementación de la lógica de tareas

Con la scaffolding completa, cree los archivos de tareas principales que definen la funcionalidad y los metadatos:

  1. Cree el archivo de definición de tarea: cree task.json en la buildandreleasetask carpeta . En este archivo se describe la tarea en el sistema de Azure Pipelines, la definición de entradas, la configuración de ejecución y la presentación de la interfaz de usuario.

    {
     "$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"
         }
     }
     }
    

    Nota:

    Reemplace por {{placeholders}} la información real de la tarea. taskguid debe ser único. Genere uno mediante PowerShell: (New-Guid).Guid

  2. Para implementar la lógica de tareas, cree index.ts con la funcionalidad principal de la tarea:

    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. Compile TypeScript en JavaScript:

    tsc
    

    El index.js archivo se crea a partir del origen de TypeScript.

Descripción de los componentes de task.json

El task.json archivo es el corazón de la definición de la tarea. Estas son las propiedades clave:

Propiedad Descripción Ejemplo
id Identificador GUID único de la tarea Generado mediante (New-Guid).Guid
name Nombre de tarea sin espacios (se usa internamente) MyCustomTask
friendlyName Nombre para mostrar que se muestra en la interfaz de usuario My Custom Task
description Descripción detallada de la funcionalidad de tareas Performs custom operations on files
author Nombre del publicador o autor My Company
instanceNameFormat Cómo aparece la tarea en los pasos de la canalización Process $(inputFile)
inputs Matriz de parámetros de entrada Consulte los siguientes tipos de entrada.
execution Especificación del entorno de ejecución Node20_1, PowerShell3, etc.
restrictions Restricciones de seguridad para comandos y variables Recomendado para nuevas tareas

Restricciones de seguridad

En el caso de las tareas de producción, agregue restricciones de seguridad para limitar el uso de comandos y el acceso a variables:

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

El modo restringido solo permite estos comandos:

  • logdetail, logissue, , complete, setprogress
  • setsecret, setvariable, , debug, settaskvariable
  • prependpath, publish

Controles de lista de permitidos de variables que se pueden establecer mediante setvariable o prependpath. Admite patrones regex básicos.

Nota:

Esta característica requiere la versión del agente 2.182.1 o posterior.

Tipos y ejemplos de entrada

Tipos de entrada comunes para parámetros de tarea:

"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"
    }
]

Prueba local de la tarea

Antes de empaquetar, pruebe la tarea para asegurarse de que funciona correctamente:

  1. Prueba con entrada que falta (debe producir un error):

    node index.js
    

    Resultado esperado:

    ##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. Prueba con entrada válida (debe realizarse correctamente):

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

    Resultado esperado:

    ##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. Control de errores de prueba:

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

    Esta acción debe desencadenar la ruta de acceso de control de errores en el código.

    Sugerencia

    Para obtener información sobre los ejecutores de tareas y las versiones de Node.js, consulte Guía de actualización del ejecutor de nodos.

Para obtener más información, consulte la referencia de tareas de compilación/lanzamiento.

2. Implementar pruebas unitarias completas

Probar la tarea exhaustivamente garantiza la confiabilidad y ayuda a detectar problemas antes de la implementación en canalizaciones de producción.

Instalación de dependencias de prueba

Instale las herramientas de prueba necesarias:

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

Creación de pruebas

  1. Cree una carpeta en el tests directorio de tareas que contenga un _suite.ts archivo:

     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
         });    
       });
    

    Sugerencia

    La carpeta de prueba debe encontrarse en la carpeta de tareas (por ejemplo, buildandreleasetask). Si se produce un error de solicitud de sincronización, instálelo en la carpeta de tareas: npm i --save-dev sync-request.

  2. Cree success.ts en el directorio de prueba para simular la ejecución correcta de tareas:

     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. Agregue la prueba correcta al _suite.ts archivo:

     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. Cree failure.ts en el directorio de prueba para probar el control de errores:

    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. Agregue la prueba de error al _suite.ts archivo:

     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();
         });
     });
    

Ejecutar las pruebas

Ejecute el conjunto de pruebas:

# Compile TypeScript
tsc

# Run tests
mocha tests/_suite.js

Ambas pruebas deben superarse. Para obtener una salida detallada (similar a la salida de la consola de compilación), establezca la variable de entorno de seguimiento:

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

Procedimientos recomendados de cobertura de pruebas

  • Probar todas las combinaciones de entrada: entradas válidas, entradas no válidas, entradas que faltan
  • Escenarios de error de prueba: errores de red, errores del sistema de archivos, problemas de permisos
  • Dependencias externas simuladas: no se basan en servicios externos en pruebas unitarias
  • Validar salidas: comprobar la salida de la consola, los resultados de la tarea y los artefactos generados
  • Pruebas de rendimiento: considere la posibilidad de agregar pruebas para tareas que procesan archivos grandes

Procedimientos recomendados de seguridad

  • Validación de entrada: valide y sane siempre las entradas.
  • Control de secretos: uso setSecret de datos confidenciales
  • Restricciones de comandos: Implementar restricciones de comandos para tareas de producción
  • Permisos mínimos: solicite solo los permisos necesarios.
  • Actualizaciones periódicas: mantener las dependencias y las versiones de Node.js actuales

Después de probar la tarea localmente e implementar pruebas unitarias completas, empaquetela en una extensión para Azure DevOps.

Instalación de herramientas de empaquetado

Instale la interfaz de la línea de comandos multiplataforma (tfx-cli):

npm install -g tfx-cli

Creación del manifiesto de extensión

El manifiesto de extensión (vss-extension.json) contiene toda la información sobre la extensión, incluidas las referencias a las carpetas de tareas e imágenes.

  1. Creación de una carpeta images con un extension-icon.png archivo

  2. Cree vss-extension.json en el directorio raíz de la extensión (no en la carpeta de tareas):

    {
     "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"
             }
         }
     ]
    }
    

Propiedades del manifiesto de clave

Propiedad Descripción
publisher Identificador del publicador de Marketplace
contributions.id Identificador único dentro de la extensión
contributions.properties.name Debe coincidir con el nombre de la carpeta de tareas.
files.path Ruta de acceso a la carpeta de tareas relativa al manifiesto

Nota:

Cambie el valor del publicador por el nombre del publicador. Para obtener información sobre cómo crear un publicador, consulte Creación del publicador.

Empaqueta tu extensión

Empaquete la extensión en un archivo .vsix:

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

Administración de versiones

  • Versión de la extensión: incremente la versión de vss-extension.json para cada actualización.
  • Versión de la tarea: incremente la versión de task.json para cada actualización de tarea.
  • Incremento automático: se usa --rev-version para incrementar automáticamente la versión de revisión
tfx extension create --manifest-globs vss-extension.json --rev-version

Importante

Tanto la versión de la tarea como la versión de la extensión deben actualizarse para que los cambios surtan efecto en Azure DevOps.

Estrategia de control de versiones

Siga los principios de control de versiones semánticos para las actualizaciones de tareas:

  • Versión principal: Cambios importantes en las entradas y salidas
  • Versión secundaria: nuevas características compatibles con versiones anteriores
  • Versión de revisión: correcciones de errores solo

Proceso de actualización:

  1. Versión de actualización task.json
  2. Versión de actualización vss-extension.json
  3. Prueba exhaustiva en una organización de prueba
  4. Publicación y supervisión de problemas

Publicación en Visual Studio Marketplace

1. Creación del publicador

  1. Inicio de sesión en el Portal de publicación de Visual Studio Marketplace
  2. Cree un nuevo publicador si se le solicita:
    • Identificador del publicador: se usa en el manifiesto de extensión (por ejemplo, mycompany-myteam)
    • Nombre para mostrar: nombre público que se muestra en Marketplace (por ejemplo, My Team)
  3. Revisión y aceptación del Contrato de publicador de Marketplace

2. Cargar la extensión

Método de interfaz web:

  1. Seleccione Upload new extension (Cargar nueva extensión)
  2. Elección del archivo empaquetado .vsix
  3. Haga clic en Cargar.

Método de línea de comandos:

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

3. Compartir la extensión

  1. Haga clic con el botón derecho en la extensión en Marketplace.
  2. Seleccione Compartir.
  3. Escriba el nombre de la organización.
  4. Incorporación de más organizaciones según sea necesario

Importante

Los publicadores deben comprobarse para compartir extensiones públicamente. Para obtener más información, consulte Package/Publish/Install.

4. Instalar en su organización

Después del uso compartido, instale la extensión en la organización de Azure DevOps:

  1. Vaya a Extensiones de configuración de> la organización.
  2. Busque la extensión.
  3. Seleccione Obtenerlo gratis e instalarlo .

3. Empaquetar y publicar la extensión

Comprobación de la extensión

Después de la instalación, compruebe que la tarea funciona correctamente:

  1. Cree o edite una canalización.
  2. Agregue la tarea personalizada:
    • Seleccione Agregar tarea en el editor de canalizaciones.
    • Buscar la tarea personalizada por nombre
    • Agréguelo a la canalización
  3. Configurar parámetros de tarea:
    • Establecimiento de entradas necesarias
    • Configuración de valores opcionales
  4. Ejecución de la canalización para probar la funcionalidad
  5. Supervisión de la ejecución:
    • Comprobación de los registros de tareas para una ejecución adecuada
    • Comprobación de las salidas esperadas
    • Asegúrese de que no hay errores ni advertencias

4. Automatizar la publicación de extensiones con CI/CD

Para mantener la tarea personalizada de forma eficaz, cree canalizaciones de compilación y versión automatizadas que controlen las pruebas, el empaquetado y la publicación.

Requisitos previos para la automatización

  • Tareas de extensión de Azure DevOps: instale la extensión de forma gratuita.
  • Grupo de variables: cree un grupo de variables de biblioteca de canalización con estas variables:
    • publisherId: identificador del publicador de Marketplace
    • extensionId: identificador de extensión de vss-extension.json
    • extensionName: nombre de extensión de vss-extension.json
    • artifactName: nombre del artefacto VSIX
  • Conexión de servicio: creación de una conexión de servicio de Marketplace con permisos de acceso de canalización

Canalización completa de CI/CD

Cree una canalización de YAML con fases completas para probar, empaquetar y publicar:

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'

Configuración de package.json para pruebas

Agregue scripts de prueba a 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"
  }
}

Desglose de la fase de canalización

Fase 1: Prueba y validación

  • Propósito: Garantizar la calidad y la funcionalidad del código
  • Acciones: Instalar dependencias, compilar TypeScript, ejecutar pruebas unitarias, publicar resultados
  • Validación: todas las pruebas deben pasar para continuar

Fase 2: Extensión de paquete

  • Propósito: Creación de un paquete VSIX que se puede implementar
  • Acciones: Consultar la versión actual, incrementar la versión, la extensión del paquete, publicar artefactos
  • Control de versiones: controla automáticamente los incrementos de versión.

Fase 3: Publicación en Marketplace

  • Propósito: Implementar en Visual Studio Marketplace
  • Condiciones: solo se ejecuta en la rama principal después del empaquetado correcto
  • Entorno: usa el entorno de implementación para las puertas de aprobación

Procedimientos recomendados para CI/CD

  • Protección de rama: solo publicar desde ramas principales o de versión
  • Puertas de entorno: uso de entornos de implementación para versiones de producción
  • Administración de versiones: automatización de incrementos de versión para evitar conflictos
  • Cobertura de pruebas: asegúrese de una cobertura completa de pruebas antes del empaquetado.
  • Seguridad: usar conexiones de servicio en lugar de credenciales codificadas de forma codificada
  • Supervisión: configuración de alertas para implementaciones con errores

En el caso de las canalizaciones de compilación clásicas, siga estos pasos para configurar el empaquetado y la publicación de extensiones:

  1. Agregue la Bash tarea para compilar TypeScript en JavaScript.

  2. Para consultar la versión existente, agregue la tarea Versión de extensión de consulta mediante las siguientes entradas:

    • Conexión a: Visual Studio Marketplace
    • Visual Studio Marketplace (conexión de servicio): conexión de servicio
    • Id. de publicador: identificador del publicador de Visual Studio Marketplace
    • Id. de extensión: Id. de la extensión en el archivo vss-extension.json
    • Aumento de la versión: Revisión
    • Variable de salida: Task.Extension.Version
  3. Para empaquetar las extensiones basadas en JSON de manifiesto, agregue la tarea Package Extension mediante las siguientes entradas:

    • Carpeta manifiestos raíz: apunta al directorio raíz que contiene el archivo de manifiesto. Por ejemplo, $(System.DefaultWorkingDirectory) es el directorio raíz.
    • Archivo de manifiesto: vss-extension.json
    • Id. de publicador: identificador del publicador de Visual Studio Marketplace
    • Id. de extensión: Id. de la extensión en el archivo vss-extension.json
    • Nombre de la extensión: nombre de la extensión en el archivo vss-extension.json
    • Versión de la extensión: $(Task.Extension.Version)
    • Invalidar la versión de tareas: activada (true)
    • Tipo de invalidación: Reemplazar solo revisión (1.0.r)
    • Visibilidad de la extensión: si la extensión todavía está en desarrollo, establezca el valor en privado. Para liberar la extensión al público, establezca el valor en público.
  4. Para copiar en archivos publicados, agregue la tarea Copiar archivos mediante las siguientes entradas:

    • Contenido: todos los archivos que se van a copiar para publicarlos como un artefacto
    • Carpeta de destino: la carpeta en la que se copian los archivos.
      • Por ejemplo: $(Build.ArtifactStagingDirectory)
  5. Agregue Publicar artefactos de compilación para publicar los artefactos y usarlos en otros trabajos o canalizaciones. Use las siguientes entradas:

    • Ruta de acceso para publicar: ruta de acceso a la carpeta que contiene los archivos que se están publicando.
      • Por ejemplo: $(Build.ArtifactStagingDirectory)
    • Nombre del artefacto: nombre proporcionado al artefacto
    • Ubicación de publicación de artefactos: elija Azure Pipelines para utilizar el artefacto en trabajos futuros.

Fase 3: Descarga de artefactos de compilación y publicación de la extensión

  1. Para instalar tfx-cli en el agente de compilación, agregue Use Node CLI for Azure DevOps (tfx-cli) (Uso de la CLI de Node para Azure DevOps [tfx-cli]).

  2. Para descargar los artefactos en un nuevo trabajo, agregue la tarea Descargar artefactos de compilación mediante las siguientes entradas:

    • Descargar artefactos generados por: si va a descargar el artefacto en un nuevo trabajo desde la misma canalización, seleccione Compilación actual. Si va a descargar en una nueva canalización, seleccione Compilación específica.
    • Tipo de descarga: elija Artefacto específico para descargar todos los archivos que se publicaron.
    • Nombre del artefacto: el nombre del artefacto publicado
    • Directorio de destino: la carpeta donde se deben descargar los archivos.
  3. Para obtener la tarea Publicar extensión , use las siguientes entradas:

    • Conexión a: Visual Studio Marketplace
    • Conexión de Visual Studio Marketplace: ServiceConnection
    • Tipo de archivo de entrada: archivo VSIX
    • Archivo VSIX: /Publisher.*.vsix
    • Id. de publicador: identificador del publicador de Visual Studio Marketplace
    • Id. de extensión: Id. de la extensión en el archivo vss-extension.json
    • Nombre de la extensión: nombre de la extensión en el archivo vss-extension.json
    • Visibilidad de la extensión: privada o pública

Opcional: Instalar y probar la extensión

Después de publicar la extensión, debe instalarse en organizaciones de Azure DevOps.

Instalación de la extensión en la organización

Instale la extensión compartida en algunos pasos:

  1. Vaya a Configuración de la organización y seleccione Extensiones.

  2. Busque la extensión en la sección Extensiones compartidas conmigo :

    • Seleccione el vínculo de extensión.
    • Seleccione Obtenerlo gratis o Instalar.
  3. Compruebe que la extensión aparece en la lista Extensiones instaladas :

    • Confirme que está disponible en la biblioteca de tareas de canalización.

Nota:

Si no ve la pestaña Extensiones , asegúrese de que está en el nivel de administración de la organización (https://dev.azure.com/{organization}/_admin) y no en el nivel de proyecto.

Pruebas integrales

Después de la instalación, realice pruebas completas:

  1. Cree una canalización de prueba:

    • Adición de la tarea personalizada a una nueva canalización
    • Configuración de todos los parámetros de entrada
    • Prueba con varias combinaciones de entrada
  2. Validar la funcionalidad:

    • Ejecución de la canalización y supervisión de la ejecución
    • Comprobación de salidas y registros de tareas
    • Comprobación del control de errores con entradas no válidas
  3. Rendimiento de la prueba:

    • Prueba con archivos de entrada grandes (si procede)
    • Supervisión del uso de recursos
    • Validación del comportamiento del tiempo de espera

Preguntas más frecuentes

P: ¿Cómo se controla la cancelación de tareas?

R: El agente de canalización envía SIGINT señales SIGTERM y a los procesos de tareas. Aunque la biblioteca de tareas no proporciona un control explícito de cancelación, la tarea puede implementar controladores de señal. Para obtener más información, consulte Cancelación de trabajos del agente.

P: ¿Cómo puedo quitar una tarea de mi organización?

R: No se admite la eliminación automática , ya que interrumpiría las canalizaciones existentes. En lugar de:

  1. En desuso de la tarea: marque la tarea como en desuso.
  2. Administración de versiones: aumentar la versión de la tarea
  3. Comunicación: notificar a los usuarios sobre la escala de tiempo de desuso

P: ¿Cómo puedo actualizar mi tarea a la versión de Node.js más reciente?

R: Actualice a la versión más reciente del nodo para mejorar el rendimiento y la seguridad. Para obtener instrucciones de migración, consulte Actualización de tareas a Node 20.

Compatibilidad con varias versiones de Node mediante la inclusión de varias secciones de ejecución en task.json:

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

Los agentes con Node 20 usan la versión preferida, mientras que los agentes anteriores vuelven al nodo 10.

Para actualizar las tareas:

  • Para asegurarse de que el código se comporta según lo previsto, pruebe las tareas en las distintas versiones del ejecutor de Node.

  • En la sección ejecución de la tarea, actualice de Node o Node10 a Node16 o Node20.

  • Para admitir versiones anteriores del servidor, debe dejar el Node/Node10 destino. Es posible que las versiones anteriores de Azure DevOps Server no tengan incluida la versión más reciente del ejecutor de Node.

  • Puede elegir compartir el punto de entrada definido en el destino o tener destinos optimizados para la versión de Node usada.

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

Importante

Si no agregas compatibilidad con el ejecutor de Node 20 a las tareas personalizadas, fallarán en los agentes instalados desde el feed de la versión de pipelines-agent-*.