Especificar trabalhos no pipeline

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

Você pode organizar seu pipeline em trabalhos. Cada pipeline tem pelo menos um trabalho. Um trabalho é uma série de etapas executadas sequencialmente como uma unidade. Em outras palavras, um trabalho é a menor unidade de trabalho que pode ser agendada para execução.

Para saber mais sobre os principais conceitos e componentes que compõem um pipeline, consulte Principais conceitos para novos usuários do Azure Pipelines.

O Azure Pipelines não oferece suporte à prioridade de trabalho para pipelines YAML. Para controlar quando os trabalhos são executados, você pode especificar condições e dependências.

Definir um trabalho único

No caso mais simples, um pipeline tem um trabalho único. Nesse caso, você não precisa usar explicitamente a palavra-chave job, a menos que esteja usando um modelo. Você pode especificar diretamente as etapas no arquivo YAML.

Esse arquivo YAML tem um trabalho executado em um agente hospedado pela Microsoft e gera as saídas Hello world.

pool:
  vmImage: 'ubuntu-latest'
steps:
- bash: echo "Hello world"

Convém especificar mais propriedades nesse trabalho. Nesse caso, você pode usar a palavra-chave job.

jobs:
- job: myJob
  timeoutInMinutes: 10
  pool:
    vmImage: 'ubuntu-latest'
  steps:
  - bash: echo "Hello world"

Seu pipeline pode ter vários trabalhos. Nesse caso, use a palavra-chave jobs.

jobs:
- job: A
  steps:
  - bash: echo "A"

- job: B
  steps:
  - bash: echo "B"

Seu pipeline pode ter várias fases, cada uma com vários trabalhos. Nesse caso, use a palavra-chave stages.

stages:
- stage: A
  jobs:
  - job: A1
  - job: A2

- stage: B
  jobs:
  - job: B1
  - job: B2

A sintaxe completa para especificar um trabalho é:

- job: string  # name of the job, A-Z, a-z, 0-9, and underscore
  displayName: string  # friendly name to display in the UI
  dependsOn: string | [ string ]
  condition: string
  strategy:
    parallel: # parallel strategy
    matrix: # matrix strategy
    maxParallel: number # maximum number simultaneous matrix legs to run
    # note: `parallel` and `matrix` are mutually exclusive
    # you may specify one or the other; including both is an error
    # `maxParallel` is only valid with `matrix`
  continueOnError: boolean  # 'true' if future jobs should run even if this job fails; defaults to 'false'
  pool: pool # agent pool
  workspace:
    clean: outputs | resources | all # what to clean up before the job runs
  container: containerReference # container to run this job inside
  timeoutInMinutes: number # how long to run the job before automatically cancelling
  cancelTimeoutInMinutes: number # how much time to give 'run always even if cancelled tasks' before killing them
  variables: { string: string } | [ variable | variableReference ] 
  steps: [ script | bash | pwsh | powershell | checkout | task | templateReference ]
  services: { string: string | container } # container resources to run as a service container

A sintaxe completa para especificar um trabalho é:

- job: string  # name of the job, A-Z, a-z, 0-9, and underscore
  displayName: string  # friendly name to display in the UI
  dependsOn: string | [ string ]
  condition: string
  strategy:
    parallel: # parallel strategy
    matrix: # matrix strategy
    maxParallel: number # maximum number simultaneous matrix legs to run
    # note: `parallel` and `matrix` are mutually exclusive
    # you may specify one or the other; including both is an error
    # `maxParallel` is only valid with `matrix`
  continueOnError: boolean  # 'true' if future jobs should run even if this job fails; defaults to 'false'
  pool: pool # agent pool
  workspace:
    clean: outputs | resources | all # what to clean up before the job runs
  container: containerReference # container to run this job inside
  timeoutInMinutes: number # how long to run the job before automatically cancelling
  cancelTimeoutInMinutes: number # how much time to give 'run always even if cancelled tasks' before killing them
  variables: { string: string } | [ variable | variableReference ] 
  steps: [ script | bash | pwsh | powershell | checkout | task | templateReference ]
  services: { string: string | container } # container resources to run as a service container
  uses: # Any resources (repos or pools) required by this job that are not already referenced
    repositories: [ string ] # Repository references to Azure Git repositories
    pools: [ string ] # Pool names, typically when using a matrix strategy for the job

Se a principal intenção do trabalho for implantar seu aplicativo (em vez de compilar ou testar seu aplicativo), você poderá usar um tipo especial de trabalho chamado trabalho de implantação.

A sintaxe de um trabalho de implantação é:

- deployment: string        # instead of job keyword, use deployment keyword
  pool:
    name: string
    demands: string | [ string ]
  environment: string
  strategy:
    runOnce:
      deploy:
        steps:
        - script: echo Hi!

Embora você possa adicionar etapas para tarefas de implantação em um job, recomendamos que você use um trabalho de implantação. Um trabalho de implantação tem alguns benefícios. Por exemplo, você pode implantar em um ambiente, que inclui benefícios como ver o histórico do que você implantou.

Tipos de trabalhos

Os trabalhos podem ser de tipos diferentes, dependendo de onde eles são executados.

  • Os trabalhos do pool de agentes são executados em um agente de um pool de agentes.
  • Os trabalhos de servidor são executados no Azure DevOps Server.
  • Os trabalhos de contêiner são executados em um contêiner em um agente de um pool de agentes. Para obter mais informações sobre como escolher os contêineres, confira Definir trabalhos de contêiner.
  • Os trabalhos do pool de agentes são executados em um agente de um pool de agentes.
  • Os trabalhos de servidor são executados no Azure DevOps Server.

Trabalhos do pool de agentes

Esses são os tipos mais comuns de trabalhos e são executados em um agente em um pool de agentes.

  • Ao usar agentes hospedados pela Microsoft, cada trabalho em um pipeline obtém um novo agente.
  • Use as demandas com agentes auto-hospedados para especificar quais recursos um agente deve ter para executar seu trabalho. Você pode obter o mesmo agente para trabalhos consecutivos, dependendo de se há mais de um agente no pool de agentes que corresponda às demandas do pipeline. Se houver apenas um agente no seu pool que corresponda às demandas do pipeline, o pipeline aguarda até que esse agente esteja disponível.

Observação

As demandas e os recursos são criados para serem usados com agentes auto-hospedados para que os trabalhos possam ser correspondidos com um agente que atenda aos requisitos do trabalho. Ao usar agentes hospedados pela Microsoft, você seleciona uma imagem para o agente que corresponde aos requisitos do trabalho. Portanto, embora seja possível adicionar recursos a um agente hospedado pela Microsoft, você não precisa usar recursos com agentes hospedados pela Microsoft.

pool:
  name: myPrivateAgents    # your job runs on an agent in this pool
  demands: agent.os -equals Windows_NT    # the agent must have this capability to run the job
steps:
- script: echo hello world

Ou várias demandas:

pool:
  name: myPrivateAgents
  demands:
  - agent.os -equals Darwin
  - anotherCapability -equals somethingElse
steps:
- script: echo hello world

Saiba mais sobre os recursos de agente.

Trabalhos de servidor

As tarefas em um trabalho de servidor são orquestradas e executadas no servidor (Azure Pipelines ou TFS). Um trabalho de servidor não requer um agente nem um computador de destino. Há suporte para apenas algumas tarefas em um trabalho de servidor no momento. O tempo máximo para um trabalho de servidor é de 30 dias.

Tarefas com suporte para trabalhos sem agente

No momento, somente as seguintes tarefas têm suporte imediato para trabalhos sem agente:

Como as tarefas são extensíveis, você pode adicionar mais tarefas sem agente usando extensões. O tempo limite padrão para trabalhos sem agente é de 60 minutos.

A sintaxe completa para especificar um trabalho de servidor é:

jobs:
- job: string
  timeoutInMinutes: number
  cancelTimeoutInMinutes: number
  strategy:
    maxParallel: number
    matrix: { string: { string: string } }

  pool: server # note: the value 'server' is a reserved keyword which indicates this is an agentless job

Você também pode usar a sintaxe simplificada:

jobs:
- job: string
  pool: server # note: the value 'server' is a reserved keyword which indicates this is an agentless job

Dependências

Ao definir vários trabalhos em uma fase única, você pode especificar dependências entre eles. Os pipelines devem conter pelo menos um trabalho sem dependências. Por padrão, os trabalhos de pipeline YAML do Azure DevOps são executados paralelamente, a menos que o valor dependsOn seja definido.

Observação

Cada agente pode executar apenas um trabalho por vez. Para executar vários trabalhos paralelamente, você deve configurar vários agentes. Você também precisa de trabalhos paralelos suficientes.

A sintaxe para definir vários trabalhos e suas dependências é:

jobs:
- job: string
  dependsOn: string
  condition: string

Trabalhos de exemplo que são compilados sequencialmente:

jobs:
- job: Debug
  steps:
  - script: echo hello from the Debug build
- job: Release
  dependsOn: Debug
  steps:
  - script: echo hello from the Release build

Trabalhos de exemplo que são compilados paralelamente (sem dependências):

jobs:
- job: Windows
  pool:
    vmImage: 'windows-latest'
  steps:
  - script: echo hello from Windows
- job: macOS
  pool:
    vmImage: 'macOS-latest'
  steps:
  - script: echo hello from macOS
- job: Linux
  pool:
    vmImage: 'ubuntu-latest'
  steps:
  - script: echo hello from Linux

Exemplo de fan-out:

jobs:
- job: InitialJob
  steps:
  - script: echo hello from initial job
- job: SubsequentA
  dependsOn: InitialJob
  steps:
  - script: echo hello from subsequent A
- job: SubsequentB
  dependsOn: InitialJob
  steps:
  - script: echo hello from subsequent B

Exemplo de fan-in:

jobs:
- job: InitialA
  steps:
  - script: echo hello from initial A
- job: InitialB
  steps:
  - script: echo hello from initial B
- job: Subsequent
  dependsOn:
  - InitialA
  - InitialB
  steps:
  - script: echo hello from subsequent

Condições

Você pode especificar as condições sob as quais cada trabalho é executado. Por padrão, um trabalho será executado se não depender de nenhum outro trabalho ou se todos os trabalhos dos quais depende forem concluídos sem erros. Você pode personalizar esse comportamento ao especificar uma condição personalizada ou ao forçar a execução de um trabalho, mesmo que um trabalho anterior falhe.

Exemplo para executar um trabalho com base no status de executar um trabalho anterior:

jobs:
- job: A
  steps:
  - script: exit 1

- job: B
  dependsOn: A
  condition: failed()
  steps:
  - script: echo this will run when A fails

- job: C
  dependsOn:
  - A
  - B
  condition: succeeded('B')
  steps:
  - script: echo this will run when B runs and succeeds

Exemplo de uso de uma condição personalizada:

jobs:
- job: A
  steps:
  - script: echo hello

- job: B
  dependsOn: A
  condition: and(succeeded(), eq(variables['build.sourceBranch'], 'refs/heads/main'))
  steps:
  - script: echo this only runs for master

Você pode especificar que um trabalho seja executado com base no valor de uma variável de saída definida em um trabalho anterior. Nesse caso, você só pode usar variáveis definidas em trabalhos diretamente dependentes:

jobs:
- job: A
  steps:
  - script: "echo '##vso[task.setvariable variable=skipsubsequent;isOutput=true]false'"
    name: printvar

- job: B
  condition: and(succeeded(), ne(dependencies.A.outputs['printvar.skipsubsequent'], 'true'))
  dependsOn: A
  steps:
  - script: echo hello from B

Tempos limite

Para evitar ocupar recursos quando seu trabalho não estiver respondendo ou estiver aguardando muito tempo, vale a pena definir um limite de tempo pelo qual seu trabalho tem permissão para ser executado. Use a configuração de tempo limite do trabalho para especificar o limite em minutos para executar o trabalho. Definir o valor como zero significa que o trabalho pode ser executado:

  • Para sempre em agentes auto-hospedados
  • Por 360 minutos (6 horas) em agentes hospedados pela Microsoft com um projeto público e repositório público
  • Por 60 minutos em agentes hospedados pela Microsoft com um projeto privado ou repositório privado (a menos que a capacidade adicional seja paga)

O tempo limite começa quando o trabalho começa a ser executado. Ele não inclui a hora em que o trabalho é enfileirado ou está aguardando um agente.

O timeoutInMinutes permite que um limite seja definido para o tempo de execução do trabalho. Quando não especificado, o padrão é 60 minutos. Quando 0 é especificado, o limite máximo é usado (descrito acima).

O cancelTimeoutInMinutes permite que um limite seja definido para o tempo de cancelamento do trabalho, quando a tarefa de implantação estiver definida para continuar em execução se uma tarefa anterior tiver falhado. Quando não especificado, o padrão é 5 minutos. O valor deve estar no intervalo de 1 a 35790 minutos.

jobs:
- job: Test
  timeoutInMinutes: 10 # how long to run the job before automatically cancelling
  cancelTimeoutInMinutes: 2 # how much time to give 'run always even if cancelled tasks' before stopping them

Os tempos limite têm o seguinte nível de precedência.

  1. Nos agentes hospedados pela Microsoft, os trabalhos são limitados em quanto tempo podem ser executados com base no tipo de projeto e se são executados usando um trabalho paralelo pago. Quando o intervalo de tempo limite do trabalho hospedado pela Microsoft expira, o trabalho é encerrado. Nos agentes hospedados pela Microsoft, os trabalhos não podem ser executados por mais tempo do que esse intervalo, independentemente de qualquer tempo limite de nível de trabalho especificado no trabalho.
  2. O tempo limite configurado no nível do trabalho especifica a duração máxima para a execução do trabalho. Quando o intervalo de tempo limite do nível de trabalho expira, o trabalho é encerrado. Se o trabalho for executado em um agente hospedado pela Microsoft, definir o tempo limite do nível do trabalho para um intervalo maior do que o tempo limite do nível de trabalho hospedado pela Microsoft interno não terá efeito e o tempo limite do trabalho hospedado pela Microsoft será usado.
  3. Você também pode definir o tempo limite de cada tarefa individualmente. Confira as opções de controle de tarefa. Se o intervalo de tempo limite no nível do trabalho decorrer antes da conclusão da tarefa, o trabalho em execução será encerrado, mesmo que a tarefa esteja configurada com um intervalo de tempo limite maior.

Configuração de vários trabalhos

Em um trabalho único que você criou, é possível executar vários trabalhos em vários agentes paralelamente. Alguns exemplos incluem:

  • Builds de várias configurações: você pode criar várias configurações paralelamente. Por exemplo, você pode criar um aplicativo do Visual C++ para as configurações debug e release nas plataformas x86 e x64. Para obter mais informações, consulte Visual Studio Build – várias configurações para várias plataformas.

  • Implantações de várias configurações: você pode executar várias implantações paralelamente, por exemplo, para regiões geográficas diferentes.

  • Teste de várias configurações: você pode executar o teste de várias configurações paralelamente.

  • As várias configurações sempre gerarão pelo menos um trabalho, mesmo que uma variável de várias configurações esteja vazia.

A estratégia matrix permite que um trabalho seja expedido várias vezes, com conjuntos de variáveis diferentes. A marca maxParallel restringe a quantidade de paralelismo. O trabalho a seguir é expedido três vezes com os valores de Localização e Navegador definidos conforme especificado. No entanto, apenas dois trabalhos são executados ao mesmo tempo.

jobs:
- job: Test
  strategy:
    maxParallel: 2
    matrix: 
      US_IE:
        Location: US
        Browser: IE
      US_Chrome:
        Location: US
        Browser: Chrome
      Europe_Chrome:
        Location: Europe
        Browser: Chrome

Observação

Os nomes de configuração de matriz (como US_IE acima) devem conter apenas letras básicas do alfabeto latino (A-Z e a-z), números) e sublinhados (_). Eles precisam iniciar com uma letra. Além disso, eles devem ter 100 caracteres ou menos.

Também é possível usar variáveis de saída para gerar uma matriz. Isso pode ser útil se você precisar gerar a matriz usando um script.

matrix aceita uma expressão de runtime que contém um objeto em cadeia de caracteres JSON. Esse objeto JSON, quando expandido, deve corresponder à sintaxe de matriz. No exemplo abaixo, codificamos a cadeia de caracteres JSON, mas ela pode ser gerada por uma linguagem de script ou programa de linha de comando.

jobs:
- job: generator
  steps:
  - bash: echo "##vso[task.setVariable variable=legs;isOutput=true]{'a':{'myvar':'A'}, 'b':{'myvar':'B'}}"
    name: mtrx
  # This expands to the matrix
  #   a:
  #     myvar: A
  #   b:
  #     myvar: B
- job: runner
  dependsOn: generator
  strategy:
    matrix: $[ dependencies.generator.outputs['mtrx.legs'] ]
  steps:
  - script: echo $(myvar) # echos A or B depending on which leg is running

Divisão

Um trabalho de agente pode ser usado para executar um conjunto de testes paralelamente. Por exemplo, você pode executar um grande conjunto de 1000 testes em um só agente. Ou pode usar dois agentes e executar 500 testes em cada um deles paralelamente.

Para aplicar a divisão, as tarefas no trabalho devem ser inteligentes o suficiente para entender a fatia à qual pertencem.

A tarefa Teste do Visual Studio é uma dessas tarefas que dá suporte à divisão de teste. Se você instalou vários agentes, poderá especificar como a tarefa Teste do Visual Studio é executada paralelamente nesses agentes.

A estratégia parallel permite que um trabalho seja duplicado muitas vezes. As variáveis System.JobPositionInPhase e System.TotalJobsInPhase são adicionadas a cada trabalho. As variáveis podem ser usadas nos seus scripts para dividir o trabalho entre os trabalhos. Confira Execução paralela e várias execuções usando trabalhos de agente.

O trabalho a seguir é expedido cinco vezes com os valores de System.JobPositionInPhase e System.TotalJobsInPhase definidos adequadamente.

jobs:
- job: Test
  strategy:
    parallel: 5

Variáveis de trabalho

Se você estiver usando YAML, as variáveis poderão ser especificadas no trabalho. As variáveis podem ser passadas para as entradas de tarefa usando a sintaxe de macro $(variableName) ou acessadas em um script usando a variável de fase.

Este é um exemplo de como definir variáveis em um trabalho e usá-las em tarefas.

variables:
  mySimpleVar: simple var value
  "my.dotted.var": dotted var value
  "my var with spaces": var with spaces value

steps:
- script: echo Input macro = $(mySimpleVar). Env var = %MYSIMPLEVAR%
  condition: eq(variables['agent.os'], 'Windows_NT')
- script: echo Input macro = $(mySimpleVar). Env var = $MYSIMPLEVAR
  condition: in(variables['agent.os'], 'Darwin', 'Linux')
- bash: echo Input macro = $(my.dotted.var). Env var = $MY_DOTTED_VAR
- powershell: Write-Host "Input macro = $(my var with spaces). Env var = $env:MY_VAR_WITH_SPACES"

Para obter informações sobre como usar uma condição, confira Especificar condições.

Workspace

Quando você executa um trabalho de pool de agentes, ele cria um workspace no agente. O workspace é um diretório no qual você baixa a origem, executa etapas e produz saídas. O diretório do workspace pode ser referenciado no seu trabalho usando variável Pipeline.Workspace. Nele, vários subdiretórios são criados:

  • Build.SourcesDirectory é onde as tarefas baixam o código-fonte do aplicativo.
  • Build.ArtifactStagingDirectory é onde as tarefas baixam os artefatos necessários para o pipeline ou carregam os artefatos antes de serem publicados.
  • Build.BinariesDirectory é onde as tarefas gravam as saídas.
  • Common.TestResultsDirectory é onde as tarefas carregam os resultados de teste.

O $(Build.ArtifactStagingDirectory) e $(Common.TestResultsDirectory) são sempre excluídos e recriados antes de cada build.

Quando você executa um pipeline em um agente auto-hospedado, por padrão, nenhum dos subdiretórios diferentes de $(Build.ArtifactStagingDirectory) e $(Common.TestResultsDirectory) é limpo entre duas execuções consecutivas. Como resultado, você pode fazer builds e implantações incrementais, desde que as tarefas sejam implementadas para fazer uso disso. Você pode substituir esse comportamento usando a configuração workspace no trabalho.

Importante

As opções de limpeza do workspace são aplicáveis somente a agentes auto-hospedados. Os trabalhos sempre são executados em um novo agente com agentes hospedados pela Microsoft.

- job: myJob
  workspace:
    clean: outputs | resources | all # what to clean up before the job runs

Quando você especifica uma das opções clean, elas são interpretadas da seguinte maneira:

  • outputs: exclua Build.BinariesDirectory antes de executar um novo trabalho.
  • resources: exclua Build.SourcesDirectory antes de executar um novo trabalho.
  • all: exclua todo o diretório Pipeline.Workspace antes de executar um novo trabalho.
  jobs:
  - deployment: MyDeploy
    pool:
      vmImage: 'ubuntu-latest'
    workspace:
      clean: all
    environment: staging

Observação

Dependendo das funcionalidades do agente e das demandas de pipeline, cada trabalho pode ser encaminhado para um agente diferente no seu pool auto-hospedado. Como resultado, você pode obter um novo agente para execuções de pipeline subsequentes (ou fases ou trabalhos no mesmo pipeline). Portanto, não limpar não é uma garantia de que execuções, trabalhos ou fases subsequentes poderão acessar saídas de execuções, trabalhos ou fases anteriores. Você pode configurar recursos de agente e demandas de pipeline para especificar quais agentes são usados para executar um trabalho de pipeline, mas, a menos que haja apenas um único agente no pool que atenda às demandas, não há garantia de que os trabalhos subsequentes usarão o mesmo agente que os trabalhos anteriores. Para obter mais informações, confira Especificar demandas.

Além da limpeza do workspace, você também pode configurar a limpeza definindo a configuração Limpar na interface do usuário das configurações do pipeline. Quando a configuração Limpar é true, que também é o valor padrão, ela é equivalente a especificar clean: true para cada etapa de check-out no seu pipeline. Ao especificar clean: true, você executará o git clean -ffdx seguido do git reset --hard HEAD, antes da busca do git. Para definir a configuração Limpar:

  1. Edite seu pipeline, escolha ... e selecione Gatilhos.

    Editar gatilhos.

  2. Selecione YAML, Obter fontes e defina a configuração Limpar desejada. O padrão é true.

    Configuração Limpar.

Download do artefato

Este arquivo YAML de exemplo publica o artefato WebSite e, em seguida, baixa o artefato em $(Pipeline.Workspace). O trabalho Implantar só será executado se o trabalho Build for bem-sucedido.

# test and upload my code as an artifact named WebSite
jobs:
- job: Build
  pool:
    vmImage: 'ubuntu-latest'
  steps:
  - script: npm test
  - task: PublishBuildArtifacts@1
    inputs:
      pathtoPublish: '$(System.DefaultWorkingDirectory)'
      artifactName: WebSite

# download the artifact and deploy it only if the build job succeeded
- job: Deploy
  pool:
    vmImage: 'ubuntu-latest'
  steps:
  - checkout: none #skip checking out the default repository resource
  - task: DownloadBuildArtifacts@0
    displayName: 'Download Build Artifacts'
    inputs:
      artifactName: WebSite
      downloadPath: $(Pipeline.Workspace)

  dependsOn: Build
  condition: succeeded()

Para obter informações sobre como usar dependsOn e condition, confira Especificar condições.

Acesso ao token OAuth

Você pode permitir que os scripts em execução em um trabalho acessem o token de segurança atual do Azure Pipelines ou TFS OAuth. O token pode ser usado para autenticar na API REST do Azure Pipelines.

O token OAuth está sempre disponível para pipelines YAML. Ele deve ser explicitamente mapeado para a tarefa ou etapa usando env. Aqui está um exemplo:

steps:
- powershell: |
    $url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/build/definitions/$($env:SYSTEM_DEFINITIONID)?api-version=4.1-preview"
    Write-Host "URL: $url"
    $pipeline = Invoke-RestMethod -Uri $url -Headers @{
      Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"
    }
    Write-Host "Pipeline = $($pipeline | ConvertTo-Json -Depth 100)"
  env:
    SYSTEM_ACCESSTOKEN: $(system.accesstoken)

O que vem a seguir