У меня есть конвейер в Azure DevOps, который анализирует мой код Ansible, чтобы обнаружить ошибки перед его объединением.
Хотя мы используем необходимые проверки статуса в GitHub, я бы хотел, чтобы любые ошибки lint добавлялись обратно в виде комментариев.
Я сталкиваюсь с двумя проблемами.
# 1 - Невозможно установить комментарий
- task: AzureCLI@2
displayName: ${{ parameters.dispName }}
continueOnError: true
inputs:
addSpnToEnvironment: true
azureSubscription: ${{ parameters.serviceConnectionName }}
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
set -euo pipefail
export ARM_CLIENT_ID=$servicePrincipalId
export ARM_CLIENT_SECRET=$servicePrincipalKey
export ARM_SUBSCRIPTION_ID=$(az account show --query id | xargs)
export ARM_TENANT_ID=$tenantId
export TF_CLI_ARGS = "-no-color"
echo $PATH
ansible --version
ansible-lint --version
ansible-lint ${{ parameters.command }} 2>&1 | tee ${{ parameters.logFile }}.log
temp=(grep -A 2 Error ${{ parameters.logFile }}.log)
if [ $? != 0 ]; then exit 1; fi
- template: comment.yaml
parameters:
comment: $temp
комментарий.yml
---
parameters:
- name: comment
type: string
steps:
- task: GitHubComment@0
displayName: 'Add github comment'
inputs:
pull-requests: write
gitHubConnection: 'github-comment'
repositoryName: '$(Build.Repository.Name)'
comment: ${{ parameters.comment }}
Этот файл comment.yaml работает, если я просто передаю статический комментарий, например «тест».
#2 — Выход из конвейера с кодом состояния 2
Если я изменю комментарий на просто статический комментарий, чтобы обойти вышеуказанную проблему, конвейер теперь завершит работу с кодом состояния 2, а не с кодом состояния 1, как я устанавливаю здесь:
if [ $? != 0 ]; then exit 1; fi
Если код существования равен 2, GitHub не помечает конвейер как сбойный в случае ошибки проверки.
-------------------------------------------------- ------------------- Вот мой обновленный код:
---
parameters:
- name: serviceConnectionName
type: string
- name: command
type: string
- name: dispName
type: string
- name: logFile
type: string
steps:
- template: init-github.yaml
- template: install-ansible-lint.yaml
- task: AzureCLI@2
displayName: ${{ parameters.dispName }}
inputs:
addSpnToEnvironment: true
azureSubscription: ${{ parameters.serviceConnectionName }}
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
set -euo pipefail
export ARM_CLIENT_ID=$servicePrincipalId
export ARM_CLIENT_SECRET=$servicePrincipalKey
export ARM_SUBSCRIPTION_ID=$(az account show --query id | xargs)
export ARM_TENANT_ID=$tenantId
export TF_CLI_ARGS = "-no-color"
echo $PATH
ansible --version
ansible-lint --version
ansible-lint ${{ parameters.command }} 2>&1 | tee ${{ parameters.logFile }}.log
error=$(grep -A 2 Error ${{ parameters.logFile }}.log)
echo "##vso[task.setvariable variable=Comment]$error"
- template: comment.yaml
parameters:
comment: $(Comment)
файл comment.yaml
---
parameters:
- name: comment
type: string
steps:
- task: GitHubComment@0
displayName: 'Add github comment'
inputs:
pull-requests: write
gitHubConnection: 'github-comment'
repositoryName: '$(Build.Repository.Name)'
comment: ${{ parameters.comment }}
- task: Bash@3
displayName: Fail Pipeline If Error
inputs:
targetType: 'inline'
script: |
if [ -n "${{ parameters.comment }}" ]; then
echo "##vso[task.complete result=Failed;]"
fi
Локальная переменная temp
в вашей задаче AzureCLI@2
недоступна в будущих задачах.
Вам необходимо добавить переменную с помощью команды регистрации Task.setvariable, чтобы следующие задачи могли использовать переменную с использованием синтаксиса макроса $(myVar)
. По умолчанию переменная будет доступна только задачам в одном задании.
Пример:
steps:
- script: |
GIT_COMMENT = "foobar"
echo "##vso[task.setvariable variable=Comment]$GIT_COMMENT"
displayName: 'Set pipeline variable: Comment'
- script: |
echo "Git comment: $(Comment)"
displayName: 'Print pipeline variable: Comment'
Как упомянул @RuiJarimba, вам нужно добавить task.setvariable logging command
, чтобы можно было использовать $(temp) в следующей задаче. Кроме того, добавьте $
к значению температуры: temp=$(grep -A 2 Failed ${{ parameters.logFile }}.log)
.
В качестве кода выхода вы можете оценить содержимое $(temp). Если оно не пусто, используйте echo "##vso[task.complete result=Failed;]"
, чтобы установить задачу как fail
, чтобы она возвращалась с ошибкой при проверке статуса Gihub.
Используйте триггер PR, поскольку GitHubComment@0
автоматически определит идентификатор PR в моем конвейере тестирования:
trigger: none
pr:
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
parameters:
- name: logFile
default: ansible-lint-log
- name: command
default: main.yml
steps:
- script: sudo apt update && sudo apt install -y ansible
displayName: 'Install Ansible'
- script: pip install ansible-lint
displayName: 'Install Ansible Lint'
- script: |
ansible --version
ansible-lint --version
displayName: 'Check Ansible and Ansible Lint versions'
- script: |
ansible-lint ${{ parameters.command }} 2>&1 | tee ${{ parameters.logFile }}.log
temp=$(grep -A 2 Failed ${{ parameters.logFile }}.log) # add $ for temp
echo "##vso[task.setvariable variable=Comment]$temp"
#if [ $? != 0 ]; then exit 1; fi
displayName: 'Lint Ansible playbook and check for errors'
- template: comment.yaml
parameters:
comment: $(Comment)
Мой шаблон yaml:
parameters:
- name: comment
type: string
steps:
- task: GitHubComment@0
displayName: 'Add github comment'
inputs:
pull-requests: write
gitHubConnection: 'GithubConn1'
repositoryName: '$(Build.Repository.Name)'
comment: '${{ parameters.comment }}'
- task: Bash@3
displayName: fail the task if there is an error
inputs:
targetType: 'inline'
script: |
if [ -n "${{ parameters.comment }}" ]; then
echo "##vso[task.complete result=Failed;]"
fi
Проверьте PR, добавленный комментарий и проверку статуса как неудавшуюся:
Пожалуйста! Вы можете воспользоваться примером конвейера (фильтровать ошибки на основе вашего журнала) для проверки, будем рады помочь. :)
Итак, я попробовал это и столкнулся с проблемами. Задача проверки завершается неудачей (как и ожидалось), но выполнение других шагов не продолжается. Если я добавлю continueonerror: true, он продолжится, но конвейер никогда не выйдет из строя. Если комментарий действительно вставлен, это просто $(Commnet). Я обновил его выше, чтобы показать новый код. @Уэйд Чжоу
Да, вам нужно использовать continueonerror: true, чтобы перейти к следующей задаче, но убедитесь, что ошибка сохранена в файле журнала. Команда ansible-lint ${{ parameters.command }} 2>&1 | tee ${{ parameters.logFile }}.log
уже должна перенаправить как стандартный вывод (stdout), так и стандартную ошибку (stderr) в файл журнала, независимо от того, успешна ли команда ansible-lint или нет. Но если вы обнаружите, что это работает не так, как ожидалось, попробуйте ansible-lint ${{ parameters.command }} > ${{ parameters.logFile }}.log 2>&1
Итак, проблема в следующем: ' ansible-lint ${{parameters.command }} 2>&1 | tee ${{parameters.logFile }}.log 'Я попробовал ваши изменения, но это не имело значения. Без него в этой структуре выходные данные, похоже, не отправляются в файл журнала, но с этим continueOnError, похоже, игнорируется. 'scriptType: bash' 'scriptLocation: inlineScript' ' continueOnError: true' Ошибка вообще не будет продолжена.
Итак, проблема на самом деле здесь temp=$(cat ansible-lint.log)
и echo "##vso[task.setvariable variable=Comment]$temp"
По какой-то причине температура кажется пустой, даже когда я знаю, что в журнале должно что-то быть.
Итак, @Wade Zhou указал мне правильное направление. Кажется, что переменная была слишком велика, чтобы ее можно было передавать между шагами, поэтому я решил ее следующим образом.
Главный трубопровод:
---
pr:
autoCancel: true
drafts: true
branches:
include:
- p0
paths:
exclude:
- README.md
- RELEASE_NOTES.md
- .azuredevops/*
- doc/*
trigger: none
pool: Lottery Production Pool
variables:
- name: serviceConnectionName
value: gcg-network-production
stages:
- stage: check
jobs:
- job: Check
steps:
- template: templates/run-ansible-lint.yaml
parameters:
serviceConnectionName: ${{ variables.serviceConnectionName }}
logFile: ansible-lint
dispName: Run ansible-lint
command: -r lint/ group_vars/*.* host_vars/*.*
- template: templates/comment.yaml
parameters:
dispName: Add Comments to PR
Шаблон ворса:
---
parameters:
- name: serviceConnectionName
type: string
- name: command
type: string
- name: dispName
type: string
- name: logFile
type: string
steps:
- template: init-github.yaml
- template: install-ansible-lint.yaml
- task: AzureCLI@2
displayName: ${{ parameters.dispName }}
inputs:
addSpnToEnvironment: true
continueOnError: true
azureSubscription: ${{ parameters.serviceConnectionName }}
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
set -euo pipefail
export ARM_CLIENT_ID=$servicePrincipalId
export ARM_CLIENT_SECRET=$servicePrincipalKey
export ARM_SUBSCRIPTION_ID=$(az account show --query id | xargs)
export ARM_TENANT_ID=$tenantId
export TF_CLI_ARGS = "-no-color"
echo $PATH
ansible --version
ansible-lint --version
ansible-lint ${{ parameters.command }} 2>&1 | tee ansible-lint.log
Шаблон комментария
---
steps:
- task: Bash@3
displayName: Extract Comment
condition: always()
inputs:
targetType: 'inline'
script: |
comment=$(sed -z 's/\n/\%0D%0A/g' ansible-lint.log)
echo "##vso[task.setvariable variable=Comment]$comment"
- task: GitHubComment@0
displayName: 'Add github comment'
condition: always()
inputs:
pull-requests: write
gitHubConnection: 'github-comment'
repositoryName: '$(Build.Repository.Name)'
comment: "$(Comment)"
Комментарий на GitHub:
Рад узнать, что проблема решена! И извините за поздний ответ из-за выходных. Да, переменные devops не поддерживают несколько строк. Хитрость заключается в том, чтобы использовать sed
для преобразования всех символов новой строки в файле ansible-lint.log. Спасибо, что поделились! Приятного кодирования!
Спасибо за супер подробный ответ!