Especificar trabajos en la canalización

Azure DevOps Services | Azure DevOps Server 2020 | Azure DevOps Server 2019 | TFS 2018

Nota:

En Microsoft Team Foundation Server (TFS) 2018 y versiones anteriores, las canalizaciones de compilación y versión se denominan definiciones, las ejecuciones se denominan compilaciones, las conexiones de servicio se denominan puntos de conexión de servicio, las fases se denominan entornos y los trabajos se denominan fases.

Puede organizar la canalización en trabajos. Cada canalización tiene al menos un trabajo. Un trabajo es una serie de pasos que se ejecutan secuencialmente como una unidad. Es decir, un trabajo es la unidad de trabajo más pequeña que se puede programar para ejecutarse.

Puede organizar la canalización de compilación o versión en trabajos. Cada canalización tiene al menos un trabajo. Un trabajo es una serie de pasos que se ejecutan secuencialmente como una unidad. Es decir, un trabajo es la unidad de trabajo más pequeña que se puede programar para ejecutarse.

Nota:

Debe instalar TFS 2018.2 para usar trabajos en procesos de compilación. En TFS 2018 RTM puede usar trabajos en procesos de implementación de versión.

Definición de un único trabajo

En el caso más sencillo, una canalización tiene un único trabajo. En ese caso, no es necesario usar explícitamente la job palabra clave a menos que use una plantilla. Puede especificar directamente los pasos del archivo YAML.

Este archivo YAML tiene un trabajo que se ejecuta en un agente hospedado por Microsoft y genera Hello world.

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

Es posible que desee especificar propiedades adicionales en ese trabajo. En ese caso, puede usar la job palabra clave .

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

La canalización puede tener varios trabajos. En ese caso, use la jobs palabra clave .

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

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

La canalización puede tener varias fases, cada una con varios trabajos. En ese caso, use la stages palabra clave .

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

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

La sintaxis completa para especificar un trabajo es:

- 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

La sintaxis completa para especificar un trabajo es:

- 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

Si la intención principal del trabajo es implementar la aplicación (en lugar de compilar o probar la aplicación), puede usar un tipo especial de trabajo denominado trabajo de implementación.

La sintaxis de un trabajo de implementación es:

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

Aunque puede agregar pasos para las tareas de implementación en job, se recomienda usar en su lugar un trabajo de implementación. Un trabajo de implementación tiene algunas ventajas. Por ejemplo, puede implementar en un entorno, lo que incluye ventajas como poder ver el historial de lo que ha implementado.

YAML no se admite en TFS.

Tipos de trabajos

Los trabajos pueden ser de diferentes tipos, en función de dónde se ejecuten.

  • Los trabajos del grupo de agentes se ejecutan en un agente de un grupo de agentes.
  • Los trabajos de servidor se ejecutan en el Azure DevOps Server.
  • Los trabajos de contenedor se ejecutan en un contenedor en un agente de un grupo de agentes. Para obtener más información sobre cómo elegir contenedores, consulte Definición de trabajos de contenedor.
  • Los trabajos del grupo de agentes se ejecutan en un agente de un grupo de agentes.
  • Los trabajos de servidor se ejecutan en el Azure DevOps Server.
  • Los trabajos del grupo de agentes se ejecutan en un agente del grupo de agentes. Estos trabajos están disponibles en canalizaciones de compilación y versión.
  • Los trabajos de servidor se ejecutan en TFS. Estos trabajos están disponibles en canalizaciones de compilación y versión.
  • Los trabajos del grupo de implementación se ejecutan en máquinas de un grupo de implementación. Estos trabajos solo están disponibles en canalizaciones de versión.

Trabajos de grupo de agentes

Estos son el tipo de trabajos más común y se ejecutan en un agente de un grupo de agentes.

  • Cuando se usan agentes hospedados por Microsoft, cada trabajo de una canalización obtiene un agente nuevo.
  • Use demandas con agentes autohospedados para especificar qué funcionalidades debe tener un agente para ejecutar el trabajo. Puede obtener el mismo agente para trabajos consecutivos, en función de si hay más de un agente en el grupo de agentes que coincida con las demandas de la canalización. Si solo hay un agente en el grupo que coincida con las demandas de la canalización, la canalización esperará hasta que este agente esté disponible.

Nota:

Las demandas y funcionalidades están diseñadas para su uso con agentes autohospedados para que los trabajos puedan coincidir con un agente que cumpla los requisitos del trabajo. Cuando se usan agentes hospedados por Microsoft, se selecciona una imagen para el agente que coincide con los requisitos del trabajo, por lo que, aunque es posible agregar funcionalidades a un agente hospedado por Microsoft, no es necesario usar funcionalidades con agentes hospedados por 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

O varias demandas:

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

YAML no se admite en TFS.

Obtenga más información sobre las funcionalidades del agente.

Trabajos de servidor

Las tareas de un trabajo de servidor se orquestan y se ejecutan en el servidor (Azure Pipelines o TFS). Un trabajo de servidor no requiere un agente ni ningún equipo de destino. Solo se admiten algunas tareas en un trabajo de servidor en la actualidad.

Tareas compatibles con trabajos sin agente

Actualmente, solo se admiten las siguientes tareas listas para los trabajos sin agente:

Dado que las tareas son extensibles, puede agregar más tareas sin agente mediante extensiones. El tiempo de espera predeterminado para los trabajos sin agente es de 60 minutos.

La sintaxis completa para especificar un trabajo de servidor es:

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

  pool: server

También puede usar la sintaxis simplificada:

jobs:
- job: string
  pool: server

YAML no se admite en TFS.

Dependencias

Al definir varios trabajos en una sola fase, puede especificar dependencias entre ellos. Pipelines debe contener al menos un trabajo sin dependencias.

Nota:

Cada agente solo puede ejecutar un trabajo cada vez. Para ejecutar varios trabajos en paralelo, debe configurar varios agentes. También necesita suficientes trabajos paralelos.

La sintaxis para definir varios trabajos y sus dependencias es:

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

Trabajos de ejemplo que se compilan secuencialmente:

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

Trabajos de ejemplo que se compilan en paralelo (sin dependencias):

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

Ejemplo de distribución ramificada:

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

Ejemplo 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

YAML no se admite en TFS.

Condiciones

Puede especificar las condiciones con las que se ejecutará cada trabajo. De forma predeterminada, un trabajo se ejecuta si no depende de ningún otro trabajo, o si todos los trabajos de los que depende se han completado y realizado correctamente. Puede personalizar este comportamiento forzando a un trabajo a ejecutarse incluso si se produce un error en un trabajo anterior o especificando una condición personalizada.

Ejemplo para ejecutar un trabajo en función del estado de ejecución de un trabajo 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

Ejemplo de uso de una condición personalizada:

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

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

Puede especificar que un trabajo se ejecute en función del valor de una variable de salida establecida en un trabajo anterior. En este caso, solo puede usar variables establecidas en trabajos dependientes directamente:

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

YAML no se admite en TFS.

Tiempos de espera

Para evitar el uso de recursos cuando el trabajo no responde o espera demasiado tiempo, es recomendable establecer un límite durante cuánto tiempo se permite ejecutar el trabajo. Use la configuración de tiempo de espera del trabajo para especificar el límite en minutos para ejecutar el trabajo. Establecer el valor en cero significa que el trabajo se puede ejecutar:

  • Forever en agentes autohospedados
  • Durante 360 minutos (6 horas) en agentes hospedados por Microsoft con un proyecto público y un repositorio público
  • Durante 60 minutos en agentes hospedados por Microsoft con un proyecto privado o repositorio privado (a menos que se pague capacidad adicional )

El período de tiempo de espera comienza cuando el trabajo comienza a ejecutarse. No incluye el tiempo en que el trabajo está en cola o está esperando a un agente.

timeoutInMinutes permite establecer un límite para el tiempo de ejecución del trabajo. Cuando no se especifica, el valor predeterminado es de 60 minutos. Cuando 0 se especifica , se usa el límite máximo (descrito anteriormente).

cancelTimeoutInMinutes Permite establecer un límite para la hora de cancelación del trabajo cuando se establece la tarea de implementación para seguir ejecutándose si se ha producido un error en una tarea anterior. Cuando no se especifica, el valor predeterminado es de 5 minutos. El valor debe estar comprendido entre 1 y 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

YAML no se admite en TFS.

Los trabajos destinados a agentes hospedados por Microsoft tienen restricciones adicionales sobre cuánto tiempo pueden ejecutarse.

También puede establecer el tiempo de espera de cada tarea individualmente; consulte las opciones de control de tareas.

Configuración de varios trabajos

Desde un único trabajo que cree, puede ejecutar varios trabajos en varios agentes en paralelo. Estos son algunos ejemplos:

  • Compilaciones de varias configuraciones: Puede crear varias configuraciones en paralelo. Por ejemplo, podría compilar una aplicación de Visual C++ para debug las configuraciones y release en ambas x86x64 plataformas. Para más información, consulte Visual Studio Compilación: varias configuraciones para varias plataformas.

  • Implementaciones de varias configuraciones: Puede ejecutar varias implementaciones en paralelo, por ejemplo, en diferentes regiones geográficas.

  • Pruebas de configuración múltiple: Puede ejecutar varias configuraciones en paralelo.

  • La configuración múltiple siempre generará al menos un trabajo, incluso si una variable de configuración múltiple está vacía.

La matrix estrategia permite que un trabajo se envíe varias veces, con diferentes conjuntos de variables. La maxParallel etiqueta restringe la cantidad de paralelismo. El siguiente trabajo se enviará tres veces con los valores de Location y Browser establecidos como se especifica. Sin embargo, solo se ejecutarán dos trabajos al mismo tiempo.

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

Nota:

Los nombres de configuración de matriz (como US_IE arriba) solo deben contener letras alfabéticas latinas básicas (A-Z, a-z), números y caracteres de subrayado (_). Deben empezar con una letra. Además, deben tener 100 caracteres o menos.

También es posible usar variables de salida para generar una matriz. Esto puede ser útil si necesita generar la matriz mediante un script.

matrix aceptará una expresión en tiempo de ejecución que contiene un objeto JSON con cadenas. Ese objeto JSON, cuando se expande, debe coincidir con la sintaxis de matriz. En el ejemplo siguiente, hemos codificado de forma rígida la cadena JSON, pero podría generarse mediante un lenguaje de scripting o un programa de línea de comandos.

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

YAML no se admite en TFS.

Segmentación

Un trabajo de agente se puede usar para ejecutar un conjunto de pruebas en paralelo. Por ejemplo, puede ejecutar un conjunto grande de 1000 pruebas en un solo agente. O bien, puede usar dos agentes y ejecutar 500 pruebas en cada una en paralelo.

Para aprovechar la segmentación, las tareas del trabajo deben ser lo suficientemente inteligentes como para comprender el segmento al que pertenecen.

La tarea Visual Studio Prueba es una tarea de este tipo que admite la segmentación de pruebas. Si ha instalado varios agentes, puede especificar cómo se ejecutará la tarea de prueba de Visual Studio en paralelo en estos agentes.

La parallel estrategia permite duplicar un trabajo muchas veces. Las variables System.JobPositionInPhase y System.TotalJobsInPhase se agregan a cada trabajo. Las variables se pueden usar en los scripts para dividir el trabajo entre los trabajos. Consulte Parallel and multiple execution using agent jobs (Ejecución en paralelo y varias ejecuciones mediante trabajos del agente).

El siguiente trabajo se enviará cinco veces con los valores de System.JobPositionInPhase y System.TotalJobsInPhase se establecerá correctamente.

jobs:
- job: Test
  strategy:
    parallel: 5

YAML no se admite en TFS.

Variables de trabajo

Si usa YAML, se pueden especificar variables en el trabajo. Las variables se pueden pasar a las entradas de tarea mediante la sintaxis de macro $(variableName) o se puede acceder a ellas dentro de un script mediante la variable stage.

Este es un ejemplo de definición de variables en un trabajo y su uso en tareas.

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"

YAML no se admite en TFS.

Para obtener información sobre el uso de una condición, vea Especificar condiciones.

Área de trabajo

Cuando se ejecuta un trabajo de grupo de agentes, se crea un área de trabajo en el agente. El área de trabajo es un directorio en el que descarga el origen, ejecuta los pasos y genera salidas. Se puede hacer referencia al directorio del área de trabajo en el trabajo mediante Pipeline.Workspace la variable . En este caso, se crean varios subdirectorios:

Cuando se ejecuta un trabajo de grupo de agentes, se crea un área de trabajo en el agente. El área de trabajo es un directorio en el que descarga el origen, ejecuta los pasos y genera salidas. Se puede hacer referencia al directorio del área de trabajo en el trabajo mediante Agent.BuildDirectory la variable . En este caso, se crean varios subdirectorios:

  • Build.SourcesDirectory es donde las tareas descargan el código fuente de la aplicación.
  • Build.ArtifactStagingDirectory es donde las tareas descargan artefactos necesarios para la canalización o cargan artefactos antes de que se publiquen.
  • Build.BinariesDirectory es donde las tareas escriben sus salidas.
  • Common.TestResultsDirectory es donde las tareas cargan sus resultados de prueba.

y $(Build.ArtifactStagingDirectory)$(Common.TestResultsDirectory) siempre se eliminan y se vuelven a crear antes de cada compilación.

Cuando se ejecuta una canalización en un agente autohospedado, de forma predeterminada, ninguno de los subdirectorios distintos $(Build.ArtifactStagingDirectory) de y $(Common.TestResultsDirectory) se limpia entre dos ejecuciones consecutivas. Como resultado, puede realizar compilaciones e implementaciones incrementales, siempre que las tareas se implementen para usarlas. Puede invalidar este comportamiento mediante la workspace configuración del trabajo.

Importante

Las opciones de limpieza del área de trabajo solo se aplican a los agentes autohospedados. Cuando se usan agentes hospedados por Microsoft, el trabajo siempre se ejecuta en un nuevo agente.

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

Al especificar una de las clean opciones, se interpretan de la siguiente manera:

  • outputs: elimine Build.BinariesDirectory antes de ejecutar un nuevo trabajo.
  • resources: elimine Build.SourcesDirectory antes de ejecutar un nuevo trabajo.
  • all: elimine todo Pipeline.Workspace el directorio antes de ejecutar un nuevo trabajo.
  jobs:
  - deployment: MyDeploy
    pool:
      vmImage: 'ubuntu-latest'
    workspace:
      clean: all
    environment: staging

Nota:

En función de las capacidades del agente y las demandas de canalización, cada trabajo se puede enrutar a otro agente del grupo autohospedado. Como resultado, puede obtener un nuevo agente para las ejecuciones de canalización posteriores (o fases o trabajos en la misma canalización), por lo que no es una garantía de que las ejecuciones, trabajos o fases posteriores puedan acceder a salidas de ejecuciones, trabajos o fases anteriores. Puede configurar las funcionalidades del agente y las demandas de canalización para especificar qué agentes se usan para ejecutar un trabajo de canalización, pero a menos que solo haya un único agente en el grupo que cumpla las demandas, no hay ninguna garantía de que los trabajos posteriores usen el mismo agente que los trabajos anteriores. Para obtener más información, vea Especificar demandas.

Además de limpiar el área de trabajo, también puede configurar la limpieza mediante la configuración Limpieza en la interfaz de usuario de configuración de canalización. Cuando la opción Limpiar es true , equivale a especificar clean: true para cada paso de desprotección de la canalización . Para configurar la opción Limpiar :

  1. Edite la canalización, elija ... y seleccione Desencadenadores.

    Edit triggers.

  2. Seleccione YAML, Obtener orígenes y configure la opción Limpiar deseada. El valor predeterminado es false.

    Clean setting.

YAML no se admite en TFS.

Descarga de artefactos

Este archivo YAML de ejemplo publica el artefacto WebSite y, a continuación, descarga el artefacto en $(Pipeline.Workspace). El trabajo Implementar solo se ejecuta si el trabajo de compilación se realiza correctamente.

# 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: $(System.DefaultWorkingDirectory)

  dependsOn: Build
  condition: succeeded()

YAML no se admite en TFS.

Para obtener información sobre el uso de dependsOn y condition, vea Especificar condiciones.

Acceso al token de OAuth

Puede permitir que los scripts que se ejecutan en un trabajo accedan al token de seguridad de OAuth de TFS o Azure Pipelines actual. El token se puede usar para autenticarse en la API rest de Azure Pipelines.

El token de OAuth siempre está disponible para las canalizaciones de YAML. Debe asignarse explícitamente a la tarea o paso mediante env. Este es un ejemplo:

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)

YAML no se admite en TFS.

Pasos siguientes