Compartilhar via


Adicionar uma extensão de tarefa de pipelines personalizados

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

Este guia explica como criar, testar e publicar tarefas personalizadas de build ou versão como extensões do Azure DevOps. As tarefas de pipeline personalizadas permitem estender o Azure DevOps com funcionalidade especializada adaptada aos fluxos de trabalho da sua equipe, desde utilitários simples até integrações complexas com sistemas externos.

Saiba como realizar as seguintes tarefas:

  • Configurar o ambiente de desenvolvimento e a estrutura do projeto
  • Criar lógica de tarefa usando o TypeScript e a Biblioteca de Tarefas do Azure Pipelines
  • Implementar testes de unidade abrangentes com estruturas simuladas
  • Empacotar sua extensão para distribuição
  • Publicar no Visual Studio Marketplace
  • Configurar pipelines automatizados de CI/CD para manutenção de extensão

Para obter mais informações sobre o Azure Pipelines, consulte o que é o Azure Pipelines?

Observação

Este artigo aborda as tarefas do agente em extensões baseadas em agentes. Para obter informações sobre tarefas de servidor e extensões baseadas em servidor, consulte Criação de Tarefas do Servidor.

Pré-requisitos

Antes de começar, verifique se você tem os seguintes requisitos em vigor:

Componente Requisito Descrição
Organização do Azure DevOps Obrigatório Criar uma organização se você não tiver uma
Editor de texto Recomendado Suporte ao Visual Studio Code para IntelliSense e depuração
Node.js Obrigatório Instalar a versão mais recente (Node.js 20 ou posterior recomendado)
Compilador TypeScript Obrigatório Instalar a versão mais recente (versão 4.6.3 ou posterior)
CLI do Azure DevOps (tfx-cli) Obrigatório Instalar usando npm i -g tfx-cli extensões de pacote
SDK de Extensão do Azure DevOps Obrigatório Instalar o pacote azure-devops-extension-sdk
Estrutura de teste Obrigatório Mocha para teste de unidade (instalado durante a instalação)

Estrutura do projeto

Crie um home diretório para seu projeto. Depois de concluir este tutorial, sua extensão deverá ter a seguinte estrutura:

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

Seu computador de desenvolvimento deve executar a versão mais recente do Node.js para garantir a compatibilidade com o ambiente de produção. Atualize o task.json arquivo para usar o Nó 20:

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

1. Crie uma tarefa personalizada

Esta seção orienta você a criar a estrutura básica e a implementação de sua tarefa personalizada. Todos os arquivos nesta etapa devem ser criados dentro da buildandreleasetask pasta dentro do diretório do home projeto.

Observação

Este passo a passo usa o Windows com o PowerShell. As etapas funcionam em todas as plataformas, mas a sintaxe da variável de ambiente é diferente. No Mac ou linux, substitua $env:<var>=<val> por export <var>=<val>.

Configurar o scaffolding de tarefas

Crie a estrutura básica do projeto e instale as dependências necessárias:

  1. Para inicializar o projeto Node.js, abra o PowerShell, vá para sua buildandreleasetask pasta e execute:

    npm init --yes
    

    O package.json arquivo é criado com as configurações padrão. O --yes sinalizador aceita todas as opções padrão automaticamente.

    Dica

    Os agentes do Azure Pipelines esperam que as pastas de tarefas incluam módulos de nó. Copie node_modules para sua buildandreleasetask pasta. Para gerenciar o tamanho do arquivo VSIX (limite de 50 MB), considere executar npm install --production ou antes do npm prune --production empacotamento.

  2. Instale a Biblioteca de Tarefas do Azure Pipelines:

    npm install azure-pipelines-task-lib --save
    
  3. Instalar definições de tipo TypeScript:

    npm install @types/node --save-dev
    npm install @types/q --save-dev
    
  4. Configurar exclusões de controle de versão

    echo node_modules > .gitignore
    

    Seu processo de build deve ser executado npm install para recompilar node_modules cada vez.

  5. Instalar dependências de teste:

    npm install mocha --save-dev -g
    npm install sync-request --save-dev
    npm install @types/mocha --save-dev
    
  6. Instalar o compilador TypeScript:

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

    Observação

    Instale o TypeScript globalmente para garantir que o tsc comando esteja disponível. Sem ele, o TypeScript 2.3.4 é usado por padrão.

  7. Configurar a compilação do TypeScript:

    tsc --init --target es2022
    

    O tsconfig.json arquivo é criado com as configurações de destino do ES2022.

Implementar a lógica da tarefa

Com o scaffolding concluído, crie os arquivos de tarefa principais que definem a funcionalidade e os metadados:

  1. Crie o arquivo de definição de tarefa: Crie task.json na buildandreleasetask pasta. Este arquivo descreve sua tarefa para o sistema do Azure Pipelines, definindo entradas, configurações de execução e apresentação de interface do usuário.

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

    Observação

    Substitua as {{placeholders}} informações reais da tarefa. O taskguid deve ser exclusivo. Gere um usando o PowerShell: (New-Guid).Guid

  2. Para implementar a lógica da tarefa, crie index.ts com a funcionalidade principal da tarefa:

    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 para JavaScript:

    tsc
    

    O index.js arquivo é criado com base na origem do TypeScript.

Noções básicas sobre componentes task.json

O task.json arquivo é o coração da sua definição de tarefa. Aqui estão as principais propriedades:

Propriedade Descrição Exemplo
id Identificador guid exclusivo para sua tarefa Gerado usando (New-Guid).Guid
name Nome da tarefa sem espaços (usado internamente) MyCustomTask
friendlyName Nome de exibição mostrado na interface do usuário My Custom Task
description Descrição detalhada da funcionalidade da tarefa Performs custom operations on files
author Nome do editor ou do autor My Company
instanceNameFormat Como a tarefa aparece nas etapas do pipeline Process $(inputFile)
inputs Matriz de parâmetros de entrada Consulte os seguintes tipos de entrada
execution Especificação do ambiente de execução Node20_1, PowerShell3etc.
restrictions Restrições de segurança para comandos e variáveis Recomendado para novas tarefas

Restrições de segurança

Para tarefas de produção, adicione restrições de segurança para limitar o uso de comandos e o acesso variável:

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

O modo restrito permite apenas estes comandos:

  • logdetail, logissue, , completesetprogress
  • setsecret, setvariable, , debugsettaskvariable
  • prependpath, publish

A lista de permissões de variáveis controla quais variáveis podem ser definidas por meio setvariable ou prependpath. Dá suporte a padrões regex básicos.

Observação

Esse recurso requer o agente versão 2.182.1 ou posterior.

Tipos de entrada e exemplos

Tipos de entrada comuns para parâmetros de tarefa:

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

Testar sua tarefa localmente

Antes de empacotar, teste sua tarefa para garantir que ela funcione corretamente:

  1. Teste com entrada ausente (deve falhar):

    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. Teste com entrada válida (deve ter êxito):

    $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. Tratamento de erros de teste:

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

    Essa ação deve disparar o caminho de tratamento de erros em seu código.

    Dica

    Para obter informações sobre executores de tarefas e versões de Node.js, consulte as diretrizes de atualização do executor do Node.

Para obter mais informações, consulte a referência da tarefa Build/release.

2. Implementar testes de unidade abrangentes

Testar sua tarefa garante completamente a confiabilidade e ajuda a detectar problemas antes da implantação em pipelines de produção.

Instalar dependências de teste

Instale as ferramentas de teste necessárias:

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

Criar teste

  1. Crie uma tests pasta no diretório de tarefas que contém um _suite.ts arquivo:

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

    Dica

    Sua pasta de teste deve estar localizada na pasta de tarefas (por exemplo, buildandreleasetask). Se você encontrar um erro de solicitação de sincronização, instale-o na pasta de tarefas: npm i --save-dev sync-request.

  2. Crie success.ts no diretório de teste para simular a execução bem-sucedida da tarefa:

     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. Adicione o teste de êxito ao arquivo _suite.ts :

     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. Crie failure.ts no diretório de teste para testar o tratamento de erros:

    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. Adicione o teste de falha ao arquivo _suite.ts :

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

Executar testes

Execute o conjunto de testes:

# Compile TypeScript
tsc

# Run tests
mocha tests/_suite.js

Ambos os testes devem ser aprovados. Para saída detalhada (semelhante à saída do console de build), defina a variável de ambiente de rastreamento:

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

Práticas recomendadas de cobertura de teste

  • Testar todas as combinações de entrada: entradas válidas, entradas inválidas, entradas necessárias ausentes
  • Cenários de erro de teste: falhas de rede, erros do sistema de arquivos, problemas de permissão
  • Dependências externas simuladas: não confie em serviços externos em testes de unidade
  • Validar saídas: verificar a saída do console, os resultados da tarefa e os artefatos gerados
  • Teste de desempenho: considere adicionar testes para tarefas que processam arquivos grandes

Melhores práticas de segurança

  • Validação de entrada: sempre valide e saniize entradas
  • Tratamento de segredos: uso setSecret para dados confidenciais
  • Restrições de comando: implementar restrições de comando para tarefas de produção
  • Permissões mínimas: solicitar somente as permissões necessárias
  • Atualizações regulares: manter as dependências e as versões Node.js atuais

Depois de testar sua tarefa localmente e implementar testes de unidade abrangentes, empacote-a em uma extensão para o Azure DevOps.

Instalar ferramentas de empacotamento

Instalar a interface de linha de comando de plataforma cruzada (tfx-cli):

npm install -g tfx-cli

Criar o manifesto da extensão

O manifesto da extensão (vss-extension.json) contém todas as informações sobre sua extensão, incluindo referências a suas pastas de tarefas e imagens.

  1. Criar uma pasta de imagens com um extension-icon.png arquivo

  2. Crie vss-extension.json no diretório raiz da extensão (não na pasta de tarefas):

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

Propriedades do manifesto de chave

Propriedade Descrição
publisher Identificador do publicador do marketplace
contributions.id Identificador exclusivo dentro da extensão
contributions.properties.name Deve corresponder ao nome da pasta de tarefas
files.path Caminho para sua pasta de tarefas em relação ao manifesto

Observação

Altere o valor do publicador para o nome do publicador. Para obter informações sobre como criar um editor, consulte Criar seu editor.

Empacotar sua extensão

Empacote sua extensão em um arquivo .vsix:

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

Gerenciamento de versão

  • Versão da extensão: incremente a versão vss-extension.json para cada atualização
  • Versão da tarefa: incremente a versão task.json para cada atualização de tarefa
  • Incremento automático: use --rev-version para incrementar automaticamente a versão do patch
tfx extension create --manifest-globs vss-extension.json --rev-version

Importante

A versão da tarefa e a versão da extensão devem ser atualizadas para que as alterações entrem em vigor no Azure DevOps.

Estratégia de controle de versão

Siga os princípios de controle de versão semântico para suas atualizações de tarefa:

  • Versão principal: alterações interruptivas em entradas/saídas
  • Versão secundária: novos recursos, compatíveis com versões anteriores
  • Versão do patch: somente correções de bug

Processo de atualização:

  1. Atualizar a versão task.json
  2. Atualizar a versão vss-extension.json
  3. Testar minuciosamente em uma organização de teste
  4. Publicar e monitorar problemas

Publicar no Visual Studio Marketplace

1. Criar seu editor

  1. Entrar no Portal de Publicação do Visual Studio Marketplace
  2. Crie um novo editor, se solicitado:
    • Identificador do publicador: usado no manifesto da extensão (por exemplo, mycompany-myteam)
    • Nome de exibição: nome público mostrado no marketplace (por exemplo, My Team)
  3. Examinar e aceitar o Contrato de Editor do Marketplace

2. Carregar sua extensão

Método de interface da Web:

  1. Selecione Carregar nova extensão
  2. Escolha seu arquivo empacotado .vsix
  3. Selecione Carregar

Método de linha de comando:

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

3. Compartilhar sua extensão

  1. Clique com o botão direito do mouse em sua extensão no marketplace
  2. Selecione Compartilhar
  3. Insira o nome da sua organização
  4. Adicionar mais organizações conforme necessário

Importante

Os editores precisam ser verificados para compartilhar extensões publicamente. Para obter mais informações, consulte Pacote/Publicação/Instalação.

4. Instalar em sua organização

Após o compartilhamento, instale a extensão em sua organização do Azure DevOps:

  1. Navegar até extensões de configurações> da organização
  2. Procurar sua extensão
  3. Selecione Obter gratuitamente e instalar

3. Empacotar e publicar sua extensão

Verificar sua extensão

Após a instalação, verifique se a tarefa funciona corretamente:

  1. Criar ou editar um pipeline.
  2. Adicione sua tarefa personalizada:
    • Selecione Adicionar tarefa no editor de pipeline
    • Pesquise sua tarefa personalizada pelo nome
    • Adicioná-lo ao pipeline
  3. Configurar parâmetros de tarefa:
    • Definir entradas necessárias
    • Definir configurações opcionais
  4. Executar o pipeline para testar a funcionalidade
  5. Monitorar a execução:
    • Verificar os logs de tarefas para a execução adequada
    • Verificar as saídas esperadas
    • Garantir que não haja erros ou avisos

4. Automatizar a publicação de extensão com CI/CD

Para manter sua tarefa personalizada com eficiência, crie pipelines de build e lançamento automatizados que lidam com testes, empacotamento e publicação.

Pré-requisitos para automação

  • Tarefas de extensão do Azure DevOps: instalar a extensão gratuitamente
  • Grupo de variáveis: crie um grupo de variáveis de biblioteca de pipeline com estas variáveis:
    • publisherId: sua ID do editor do marketplace
    • extensionId: ID da extensão de vss-extension.json
    • extensionName: nome da extensão de vss-extension.json
    • artifactName: nome do artefato VSIX
  • Conexão de serviço: criar uma conexão de serviço do Marketplace com permissões de acesso de pipeline

Concluir pipeline de CI/CD

Crie um pipeline YAML com estágios abrangentes para teste, empacotamento e publicação:

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'

Configurar package.json para teste

Adicione scripts de teste ao seu 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"
  }
}

Detalhamento do estágio do pipeline

Estágio 1: Testar e validar

  • Finalidade: garantir a qualidade e a funcionalidade do código
  • Ações: instalar dependências, compilar TypeScript, executar testes de unidade, publicar resultados
  • Validação: todos os testes devem ser aprovados para continuar

Estágio 2: Extensão do pacote

  • Finalidade: criar um pacote VSIX implantável
  • Ações: Consultar a versão atual, incrementar versão, extensão do pacote, publicar artefatos
  • Controle de versão: manipula automaticamente incrementos de versão

Estágio 3: Publicar no marketplace

  • Finalidade: implantar no Visual Studio Marketplace
  • Condições: Só é executado no branch principal após o empacotamento bem-sucedido
  • Ambiente: usa o ambiente de implantação para portões de aprovação

Práticas recomendadas para CI/CD

  • Proteção de ramificação: publicar somente de branches principais/versões
  • Portões de ambiente: usar ambientes de implantação para versões de produção
  • Gerenciamento de versão: automatizar incrementos de versão para evitar conflitos
  • Cobertura de teste: garantir uma cobertura de teste abrangente antes do empacotamento
  • Segurança: usar conexões de serviço em vez de credenciais codificadas
  • Monitoramento: configurar alertas para implantações com falha

Para pipelines de build clássicos, siga estas etapas para configurar o empacotamento e a publicação de extensão:

  1. Adicione a tarefa Bash para compilar o TypeScript em JavaScript.

  2. Para consultar a versão existente, adicione a tarefa Versão da Extensão de Consulta usando as seguintes entradas:

    • Conectar-se a: Visual Studio Marketplace
    • Visual Studio Marketplace (conexão de serviço): conexão de serviço
    • ID do Publicador: ID do publicador do Visual Studio Marketplace
    • ID da extensão: ID da extensão no arquivo vss-extension.json
    • Aumentar versão: patch
    • Variável de saída: Task.Extension.Version
  3. Para empacotar as extensões com base no Json de manifesto, adicione a tarefa Extensão de Pacote usando as seguintes entradas:

    • Pasta de manifestos raiz: aponta para o diretório raiz que contém o arquivo de manifesto. Por exemplo, $(System.DefaultWorkingDirectory) é o diretório raiz
    • Arquivo de manifesto: vss-extension.json
    • ID do Publicador: ID do publicador do Visual Studio Marketplace
    • ID da extensão: ID da extensão no arquivo vss-extension.json
    • Nome da extensão: Nome da sua extensão no arquivo vss-extension.json
    • Versão da extensão: $(Task.Extension.Version)
    • Substituir versão das tarefas: marcada (true)
    • Tipo de substituição: apenas substituir o patch (1.0.r)
    • Visibilidade da extensão: se a extensão ainda estiver em desenvolvimento, defina o valor como privado. Para liberar a extensão para o público, defina o valor como público.
  4. Para copiar para arquivos publicados, adicione a tarefa Copiar arquivos usando as seguintes entradas:

    • Conteúdo: Todos os arquivos a serem copiados para publicá-los como um artefato
    • Pasta de destino: a pasta para a qual os arquivos são copiados
      • Por exemplo: $(Build.ArtifactStagingDirectory)
  5. Adicione Publicar artefatos de build para disponibilizar os artefatos para uso em outros trabalhos ou pipelines. Use as seguintes entradas:

    • Caminho para publicar: o caminho para a pasta que contém os arquivos que estão sendo publicados
      • Por exemplo: $(Build.ArtifactStagingDirectory)
    • Nome do artefato: O nome dado ao artefato
    • Local de publicação de artefatos: escolha Pipelines do Azure para usar o artefato em trabalhos futuros

Etapa 3: Baixar artefatos de build e publicar a extensão

  1. Para instalar o tfx-cli em seu agente de build, adicione Use Node CLI para Azure DevOps (tfx-cli).

  2. Para baixar os artefatos em um novo trabalho, adicione a tarefa Baixar artefatos de compilação usando as seguintes entradas:

    • Baixe artefatos produzidos por: se você estiver baixando o artefato em um novo trabalho no mesmo pipeline, selecione Build atual. Se você estiver baixando em um novo fluxo de trabalho, selecione Build específico
    • Tipo de download: escolha um artefato específico para baixar todos os arquivos que foram publicados.
    • Nome do artefato: o nome do artefato publicado
    • Diretório de destino: a pasta em que os arquivos devem ser baixados
  3. Para obter a tarefa Publicar Extensão, use as seguintes entradas:

    • Conectar-se a: Visual Studio Marketplace
    • Conexão do Visual Studio Marketplace: ServiceConnection
    • Tipo de arquivo de entrada: arquivo VSIX
    • Arquivo VSIX: /Publisher.*.vsix
    • ID do Publicador: ID do publicador do Visual Studio Marketplace
    • ID da extensão: ID da extensão no arquivo vss-extension.json
    • Nome da extensão: Nome da sua extensão no arquivo vss-extension.json
    • Visibilidade da extensão: privada ou pública

Opcional: Instale e teste sua extensão

Depois de publicar sua extensão, ela precisa ser instalada em organizações do Azure DevOps.

Instalar a extensão para a organização

Instale sua extensão compartilhada em algumas etapas:

  1. Vá para as configurações da organização e selecione Extensões.

  2. Localize sua extensão na seção Extensões Compartilhadas Comigo :

    • Selecione o link de extensão
    • Selecione Obter gratuitamente ou Instalar
  3. Verifique se a extensão aparece em sua lista de extensões instaladas :

    • Confirme se ele está disponível em sua biblioteca de tarefas de pipeline

Observação

Se você não vir a guia Extensões , verifique se está no nível de administração da organização (https://dev.azure.com/{organization}/_admin) e não no nível do projeto.

Testar ponta a ponta

Após a instalação, execute um teste abrangente:

  1. Criar um pipeline de teste:

    • Adicionar sua tarefa personalizada a um novo pipeline
    • Configurar todos os parâmetros de entrada
    • Testar com várias combinações de entrada
  2. Validar a funcionalidade:

    • Executar o pipeline e monitorar a execução
    • Verificar saídas e logs de tarefas
    • Verificar o tratamento de erros com entradas inválidas
  3. Desempenho do teste:

    • Testar com arquivos de entrada grandes (se aplicável)
    • Monitorar o uso de recursos
    • Validar o comportamento do tempo limite

Perguntas frequentes

P: Como o cancelamento de tarefa é tratado?

R: O agente de pipeline envia SIGINT e SIGTERM sinaliza para processos de tarefa. Embora a biblioteca de tarefas não forneça tratamento explícito de cancelamento, sua tarefa pode implementar manipuladores de sinal. Para obter detalhes, consulte o cancelamento de trabalhos do Agente.

P: Como posso remover uma tarefa da minha organização?

R: A exclusão automática não tem suporte , pois interromperia os pipelines existentes. Em vez disso:

  1. Preterir a tarefa: marcar a tarefa como preterida
  2. Gerenciamento de versão: aumentar a versão da tarefa
  3. Comunicação: notificar os usuários sobre a linha do tempo de substituição

P: Como posso atualizar minha tarefa para a versão mais recente do Node.js?

R: Atualize para a versão mais recente do Nó para obter melhor desempenho e segurança. Para obter diretrizes de migração, consulte Atualizar tarefas para o Nó 20.

Dê suporte a várias versões do Nó , incluindo várias seções de execução em task.json:

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

Agentes com o Node 20 usam a versão preferencial, enquanto os agentes mais antigos retornam para o Nó 10.

Para atualizar suas tarefas:

  • Para garantir que seu código se comporte conforme o esperado, teste suas tarefas nas várias versões do Node.

  • Na seção de execução da tarefa, atualize de Node ou Node10 para Node16 ou Node20.

  • Para oferecer suporte a versões mais antigas do servidor, você deve deixar o destino Node/Node10. As versões mais antigas do Servidor Azure DevOps podem não ter a versão mais recente do runner do Node incluída na instalação.

  • Você pode optar por compartilhar o ponto de entrada definido no destino ou ter destinos otimizados para a versão do Node.js usada.

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

Importante

Se você não adicionar suporte para o executor de tarefas Node 20 às suas tarefas personalizadas, elas falharão nos agentes instalados no pipelines-agent-*feed de lançamento.