Не понимаю, почему опубликованные артефакты работают в одном случае, а не в другом для конвейеров Azure DevOps

#azure-devops #azure-pipelines #azure-pipelines-yaml

Вопрос:

Обзор

Я использую артефакты для сохранения некоторых файлов и сборок на разных этапах. В одном случае он работает, а в другом-нет, несмотря на то, что он может видеть, что артефакт опубликован.

Что Работает

Например, это загружает мои k8s манифесты в артефакт, чтобы у них был доступ на deployment этапе к AKS, и это отлично работает:

 # publishStage.yaml
stages:
- stage: Publish
  displayName: Publish artifacts
  dependsOn: 
  - SeleniumTests
  - Changed
  condition: succeeded()
  variables:
    anyServicesChanged: $[ stageDependencies.Changed.Changes.outputs['detectChanges.anyServicesChanged'] ]
    anyConfigsChanged: $[ stageDependencies.Changed.Changes.outputs['detectChanges.anyConfigsChanged'] ]
  jobs:
  - job: Publish
    condition: or(eq(variables.anyServicesChanged, true), eq(variables.anyConfigsChanged, true), eq(variables['Build.Reason'], 'Manual'))
    displayName: Publishing artifacts...
    steps:
    - upload: k8s
      artifact: k8s
 
 # deployStage.yaml
parameters:
- name: tag
  default: ''
- name: tagVersion
  default: ''

stages:
- stage: Deploy
  displayName: Deployment stage...
  dependsOn: 
  - Publish
  - Changed
  condition: succeeded()
  variables:
    anyServicesChanged: $[ stageDependencies.Changed.Changes.outputs['detectChanges.anyServicesChanged'] ]
    anyConfigsChanged: $[ stageDependencies.Changed.Changes.outputs['detectChanges.anyConfigsChanged'] ]
    servicesChanged: $[ stageDependencies.Changed.Changes.outputs['detectChanges.servicesChanged'] ]
  jobs:
  - deployment: Deploy
    condition: or(eq(variables.anyServicesChanged, true), eq(variables.anyConfigsChanged, true), eq(variables['Build.Reason'], 'Manual'))
    displayName: Deploying services...
    environment: 'App Production AKS'
    strategy:
      runOnce:
        deploy:
          steps:
          - task: KubernetesManifest@0
            displayName: Create imagePullSecret
            inputs:
              action: createSecret
              secretName: $(imagePullSecret)
              kubernetesServiceConnection: 'App Production AKS'
              dockerRegistryEndpoint: $(dockerRegistryServiceConnection)
          - template: deployStep.yaml
            parameters: 
              tag: ${{ parameters.tag }}
              tagVersion: ${{ parameters.tagVersion }}
              serviceName: api-v1
              pathName: api
 
 # deployStep.yaml
parameters:
- name: tag
  default: ''
- name: tagVersion
  default: ''
- name: serviceName
  default: ''
- name: pathName
  default: ''

steps:
- task: KubernetesManifest@0
  condition: contains(variables['servicesChanged'], '${{ parameters.serviceName }}')
  displayName: Deploy to ${{ parameters.pathName }} Kubernetes cluster...
  inputs:
    action: deploy
    kubernetesServiceConnection: 'App Production AKS'
    manifests: |
      $(Pipeline.Workspace)/k8s/aks/${{ parameters.pathName }}.yaml
    imagePullSecrets: |
      $(imagePullSecret)
    containers: |
      $(containerRegistry)/$(imageRepository)-${{ parameters.pathName }}:${{ parameters.tag }}-${{ parameters.tagVersion }}
 

What Doesn’t Work

However, now what I’m trying to do is build a service in the application, run unit tests, and take the exact same build that was tested and use it for building the Docker image.

I’m trying to do this with the following:

 # unitTestsStage.yaml
stages:
- stage: UnitTests
  displayName: Run unit tests for services...
  dependsOn: Changed
  condition: succeeded()
  jobs:
  - template: ../secretsJob.yaml
  - template: pythonJob.yaml
    parameters:
      serviceName: api-v1
      pathName: api
 
 # pythonJob.yaml
parameters:
- name: serviceName
  type: string
  default: ''
- name: pathName
  type: string
  default: ''

jobs:
- job: UnitTests
  displayName: Running unit tests for ${{ parameters.serviceName }}...
  variables:
    servicesChanged: $[ stageDependencies.Changed.Changes.outputs['detectChanges.servicesChanged'] ]
  condition: or(contains(variables['servicesChanged'], '${{ parameters.serviceName }}'), eq(variables['Build.Reason'], 'Manual'))
  dependsOn: Secrets
  steps:
  - task: UsePythonVersion@0
    inputs:
      versionSpec: '3.8'
  - script: |
      cd ${{ parameters.pathName }} amp;amp; 
      python -m pip install --upgrade pip amp;amp; 
      pip install -r requirements.txt
    displayName: Install requirements for ${{ parameters.pathName }}...
  - script: cd ${{ parameters.pathName }} amp;amp;  coverage run --omit='manage.py,config/*,.venv*,*/*__init__.py,*/tests.py,*/admin.py' manage.py test amp;amp; coverage report
    displayName: Run unit tests and coverage for ${{ parameters.pathName }}...
    env:
      DJANGO_SECRET_KEY: $(PROD-DJANGOSECRETKEY)
      DJANGO_DEBUG: $(PROD-DJANGODEBUG)
      DOMAIN: $(PROD-DOMAIN)
      PGDATABASE: $(PROD-PGDATABASE)
      PGDATABASEV2: $(PROD-PGDATABASEV2)
      PGUSER: $(PROD-PGUSER)
      PGPASSWORD: $(PROD-PGPASSWORD)
      PGHOST: $(PROD-PGHOST)
      PGPORT: $(PROD-PGPORT)
  - upload: ${{ parameters.pathName }}
    artifact: ${{ parameters.pathName }}
    condition: succeeded()

 
 # buildStage.yaml
parameters:
- name: tag
  default: ''
- name: tagVersion
  default: ''
#
stages:
- stage: BuildAndPush
  displayName: Build and Push Docker images of services...
  dependsOn: 
  - UnitTests
  - Changed
  condition: succeeded()
  variables:
    anyServicesChanged: $[ stageDependencies.Changed.Changes.outputs['detectChanges.anyServicesChanged'] ]
    servicesChanged: $[ stageDependencies.Changed.Changes.outputs['detectChanges.servicesChanged'] ]
  jobs:
  - job: BuildAndPush
    condition: or(eq(variables.anyServicesChanged, true), eq(variables['Build.Reason'], 'Manual'))
    displayName: Building and Push Docker images of services...
    steps:
    - template: buildStep.yaml
      parameters: 
        tag: ${{ parameters.tag }}
        tagVersion: ${{ parameters.tagVersion }}
        serviceName: api-v1
        pathName: api
 
 # buildStep.yaml
parameters:
- name: tag
  default: ''
- name: tagVersion
  default: ''
- name: serviceName
  default: ''
- name: pathName
  default: ''

steps: 
- task: Docker@2
  condition: contains(variables['servicesChanged'], '${{ parameters.serviceName }}')
  displayName: Build and Push ${{ parameters.pathName }} Docker image
  inputs:
    command: buildAndPush
    repository: $(imageRepository)-${{ parameters.pathName }}
    dockerfile: $(Pipeline.Workspace)/${{ parameters.pathName }}/Dockerfile
    buildContext: $(Pipeline.Workspace)/${{ parameters.pathName }}
    containerRegistry: $(dockerRegistryServiceConnection)
    tags: |
      ${{ parameters.tag }}-${{ parameters.tagVersion }}
 

Я вижу api , что артефакт был опубликован:

введите описание изображения здесь

Но потом, когда дело доходит до того, чтобы на самом деле вытащить его для создания образа Докера, я просто получаю:

 Starting: Build and Push api Docker image
==============================================================================
Task         : Docker
Description  : Build or push Docker images, login or logout, start or stop containers, or run a Docker command
Version      : 2.187.0
Author       : Microsoft Corporation
Help         : https://aka.ms/azpipes-docker-tsg
==============================================================================
##[error]Unhandled: No Dockerfile matching  /home/vsts/work/1/api/Dockerfile  was found.
Finishing: Build and Push api Docker image
 

Если бы у меня была задача по сценарию ls -la $(Pipeline.Workspace) , я бы получил:

 drwxr-xr-x  6 vsts docker 4096 Jul  9 16:19 .
drwxr-xr-x  7 vsts root   4096 Jul  9 16:19 ..
drwxr-xr-x  2 vsts docker 4096 Jul  9 16:19 TestResults
drwxr-xr-x  2 vsts docker 4096 Jul  9 16:19 a
drwxr-xr-x  2 vsts docker 4096 Jul  9 16:19 b
drwxr-xr-x 12 vsts docker 4096 Jul  9 16:19 s
 

Вопрос

Так что же я здесь делаю не так, что ссылка на артефакт работает в одном случае, а не в другом?

Ответ №1:

Задание развертывания в первом случае автоматически загружает все опубликованные артефакты.

Во втором случае вы используете обычную работу, которая не загружает ее. Вы должны сделать это неявно, добавив шаг загрузки перед запуском шага настройки..