A grande maioria das pessoas irá trabalhar com esteiras relativamente lineares do Jenkins - ainda que estas esteiras chamem outras esteiras, que chamem outras esteiras, e outras, e assim por diante, criando verdadeiros monstros dos espaguetis voadores.
Entretanto há uma coisa que Jenkins - seja por design ou por qualquer outro motivo inexplicável - simplesmente não faz bem: manter controle sobre os status dos stages durante a execução do build. Uma das consequências disso é o fato de que o comportamento padrão do Jenkins é falhar uma esteira quando qualquer um dos seus stages falha. E cabe a você, a pessoa que está desenvolvendo a esteira, cuidar disso.
Durante este post, irei abordar alguns cenários comuns e algumas soluções possíveis para os mesmos.
Primeiro cenário: continuar um build mesmo quando um stage falha
Esta é uma pergunta muito comum na stack mais famosa do mundo. Imagine que você possui um stage que pode falhar, mas que não necessariamente impedirá a execução da esteira como um todo. Se você ama Java, você simplesmente irá adorar o quê vem a seguir.
Para o cenário, iremos trabalhar com a seguinte pipeline:
pipeline {
agent any
stages {
stage('Primeiro stage') {
steps {
script {
git credentialsId: 'Bitbucket', url: 'https://bitbucket.org/stefano/bla.git'
}
}
}
stage('Segundo stage') {
steps {
script {
print('Ooopsie, nunca vou rodar! :(')
}
}
}
}
}
Esta pipeline intencionalmente irá falhar, uma vez que o repositório especificado na linha 8 não existe. Na apresentação visual da pipeline, teremos algo parecido com isto:
Primeira solução: utilizar um bloco try {} catch() {}
Podemos utilizar um bloco try {} catch() {}
ao redor do trecho passível de erro.
pipeline {
agent any
stages {
stage('Primeiro stage') {
steps {
script {
try {
git credentialsId: 'Bitbucket', url: 'https://bitbucket.org/stefano/bla.git'
}
catch(Exception e) {
print("${e}")
}
}
}
}
stage('Segundo stage') {
steps {
script {
print('Ooopsie, nunca vou rodar! :(')
print('Opa pera! :)')
}
}
}
}
}
E a apresentação da pipeline:
Como pode-se observar, ambos os stages são executados, contudo o primeiro é apresentado com sucesso, ainda que o comando tenha apresentado falha. Isso é uma questão de semântica: houve um erro em tempo de execução da pipeline e houve uma tratativa quanto a ele, logo, há o entendimento de que está tudo bem e que o stage deve ser marcado como SUCCESS.
Segunda solução: Quando queremos marcar o stage como FAILED
Caso desejemos marcar aquele stage como FAILED, devemos utilizar um bloco catchError()
, podendo utilizá-lo juntamente com um bloco try {} catch() {}
ou sozinho. O quê o catchError()
faz é pegar o resultado de um step (ou steps) que estão dentro do bloco e caso algum destes termine em erro, ele setará os status do stage ou o build conforme o seu desejo, normalmente sendo como SUCCESS ou FAILURE. Abaixo dois exemplos, um não aninhado e o outro com um bloco try {} catch() {}
, assim como suas respectivas execuções:
pipeline {
agent any
stages {
stage('Primeiro stage') {
steps {
script {
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
git credentialsId: 'Bitbucket', url: 'https://bitbucket.org/stefano/bla.git'
}
}
}
}
stage('Segundo stage') {
steps {
script {
print('Ooopsie, nunca vou rodar! :(')
print('Opa, vou sim! :)')
}
}
}
}
}
pipeline {
agent any
stages {
stage('Primeiro stage') {
steps {
script {
try {
sh 'cat /tmp'
}
catch(Exception e) {
print("${e}")
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
sh 'exit 1'
}
}
}
}
}
stage('Segundo stage') {
steps {
script {
print('Ooopsie, nunca vou rodar! :(')
print('Opa, vou sim!')
}
}
}
}
}
Segundo cenário: Execução de um stage de forma condicional
Imagine que você deseja que um stage seja executado condicionalmente, dependendo do status do anterior. A primeira solução que nos vêm à mente é trabalhar com blocos when {}
. Infelizmente, ela não é viável, pois os mesmos são analisados no começo da execução da pipeline.
A segunda solução é criar algumas variáveis de ambiente em bloco environment {}
e analisá-las de um bloco para o outro, o quê também não daria certo, pois não conseguimos trocar seus valores.
Chegamos então à uma solução possível que é: Arquivos texto. Abaixo um exemplo:
pipeline {
agent any
stages {
stage("stage1") {
steps {
script {
try {
sh 'cat /tmp'
writeFile encoding: 'utf-8', file: 'stageStatus', text: 'SUCCESS'
}
catch(Exception e) {
writeFile encoding: 'utf-8', file: 'stageStatus', text: 'FAILED'
print("Ooopsie!")
catchError(buildResult: 'SUCCESS', stageResult: 'FAILURE') {
sh 'exit 1'
}
}
}
}
}
stage("stage2") {
steps {
script {
def previousStageStatus = readFile encoding: 'utf-8', file: 'stageStatus'
if (previousStageStatus == 'FAILED') {
print('The previous stage failed')
}
}
}
}
}
}
Nele, estamos utilizando as funções writeFile() e readFile() para respectivamente escrever e ler o nosso arquivo texto, que conterá o status do primeiro stage e, caso seja FAILURE, o corpo do segundo stage será executado.
O único problema desta solução - que não é grande, pelo menos da minha perspectiva - é que não importa o quê aconteça, o segundo stage sempre será executado, ainda que ele não faça nada.
Pipeline quando o stage é disparado
Pipeline quando o stage não é disparado
Bom pessoal, por hoje é só. Espero que tenham gostado!
Abraços!
Top comments (0)