Jenkinsfile
como código vivo.
Transforme seus pipelines em código Groovy auditável e seguro: domine as sintaxes Declarativa e Scripted, injete segredos sem vazar no console, execute Checkstyle, Hadolint, JUnit, Cobertura, OWASP, Snyk e SonarQube em paralelo, publique artefatos no Nexus com migrações Flyway e escale para dezenas de projetos com Multibranch e Shared Libraries.
Trilha de formação: Domínio do Jenkins da arquitetura ao deploy automatizado
Na Etapa 2, saímos da configuração inicial do servidor e mergulhamos na construção completa do pipeline de Integração Contínua em código (Jenkinsfile), integrando qualidade, cobertura, segurança de aplicações e imagens Docker (conectadas ao nosso curso Docker em Produção com Critério), bancos de dados e bibliotecas compartilhadas.
Percurso da Etapa 2 (Jenkinsfile, Credenciais e CI Completo)
Todos os snippets de Jenkinsfile e validadores em Python abaixo foram desenhados para você copiar, adaptar e usar diretamente nos seus repositórios.
Domine a estrutura do Jenkinsfile em Groovy, compare Declarative e Scripted Pipeline e acelere jobs executando estágios independentes em paralelo.
OFICINA 02Cofre de Credenciais e Mascaramento ↗Proteja senhas, tokens e chaves SSH por escopo (Global, System, Folder), injete com credentials() e withCredentials e evite vazamentos por interpolação Groovy.
OFICINA 03Checkstyle, Hadolint e warnings-ng ↗Audite simultaneamente o padrão do código-fonte e as boas práticas do Dockerfile (Hadolint), publicando gráficos e Quality Gates com recordIssues.
OFICINA 04Testes JUnit e Cobertura (Cobertura) ↗Automatize testes unitários em contêineres isolados e imponha metas mínimas para Line Coverage, Method Coverage e Condition Coverage.
OFICINA 05OWASP, Snyk, SonarQube e Simulador ↗Varra vulnerabilidades de pacotes (OWASP), camadas de imagem Docker (Snyk/Trivy) e código (SonarQube), e simule o ganho de paralelismo no pipeline.
OFICINA 06Artefatos no Nexus e DB com Flyway ↗Empacote e publique artefatos versionados no Sonatype Nexus ou Docker Registry e aplique migrações de banco (Database as Code) com Flyway.
OFICINA 07Multibranch, Condicionais e Slack ↗Converta jobs para Multibranch Pipeline com filtros when por branch e configure notificações inteligentes (post) para Slack e E-mail.
OFICINA 08Shared Libraries (@Library) ↗Estruture bibliotecas compartilhadas em Groovy (vars/, src/, resources/) para padronizar dezenas de microsserviços com apenas duas linhas no Jenkinsfile.
“O Jenkinsfile é a fonte única da verdade para todas as mudanças e execuções do seu fluxo de trabalho.”
Nos Capítulos 3, 4 e 6 de CI/CD Pipeline with Docker and Jenkins, Sandeep Rawat demonstra como sair de scripts manuais espalhados para um pipeline versionado, seguro e modular que inspeciona código, contêineres e banco de dados a cada commit.Pipeline como Código: Declarative vs. Scripted Pipeline, Termos Chave e Execução Paralela
Um colega edita um script diretamente na caixa de texto da interface web do Jenkins às 18h, quebra o build de produção e ninguém sabe o que estava escrito antes nem quem alterou. Como transformar o pipeline em código versionado, revisável e paralelo?
Por que o `Jenkinsfile` no Git é a Fonte Única da Verdade (Seção 3.7)
Na Seção 3.7, Sandeep Rawat estabelece a regra de ouro da automação moderna no Jenkins: embora você possa digitar o script do pipeline diretamente na tela de configuração do job (útil apenas para testes rápidos), em qualquer projeto real a definição do pipeline deve morar em um arquivo texto chamado Jenkinsfile na raiz do repositório Git. Escrito na linguagem Groovy (baseada na plataforma Java, assim como o próprio Jenkins) e apoiado nos plugins workflow-aggregator e pipeline-utility-steps, o Jenkinsfile entrega quatro vantagens decisivas:
- Histórico e Reversão Imutáveis: toda alteração no pipeline passa por Pull Request, revisão de pares e pode ser revertida em segundos com
git revert; - Descoberta Automática por Branch: permite que o Jenkins crie automaticamente pipelines para cada nova branch e Pull Request;
- Pausa e Controle de Fluxo: permite pausar o pipeline no meio da execução aguardando um clique humano de aprovação (
input) antes de avançar para um ambiente sensível; - Execução Sequencial e Paralela: permite encadear etapas dependentes em sequência e disparar etapas independentes ao mesmo tempo com o bloco
parallel.
Scripted Pipeline vs. Declarative Pipeline e os 5 Termos Fundamentais
O livro compara as duas sintaxes oficiais suportadas pelo Jenkinsfile e define seus cinco blocos estruturais (pipeline, node/agent, stage, steps e parallel):
| Característica | Declarative Pipeline (Padrão Moderno) | Scripted Pipeline (Tradicional Groovy) |
|---|---|---|
| Bloco Raiz e Alocação de Nó | Inicia obrigatoriamente com pipeline { agent any ... } (ou agent { label '...' } / agent { docker '...' }). | Inicia diretamente com o bloco node('label-do-agente') { ... }. |
| Estrutura de Tarefas | Exige a hierarquia explícita stages { stage('Nome') { steps { ... } } }. | Usa blocos stage('Nome') { ... } contendo comandos e código Groovy diretamente, sem bloco steps. |
| Legibilidade e Validação | Mais fácil de ler, manter e validar sintaticamente antes de rodar; inclui blocos nativos post, environment, when e tools. | Oferece flexibilidade máxima de programação imperativa em Groovy (loops for, try/catch/finally nativos). |
| Como combinar o melhor dos dois? | Use Declarative Pipeline como estrutura principal e, quando precisar de lógica Groovy imperativa, abra um bloco script { ... } dentro de steps! | Usado em pipelines legados ou dentro de métodos internos de Shared Libraries. |
// Exemplo prático unindo estrutura Declarativa, ferramentas gerenciadas,
// execução em PARALELO (reduzindo o tempo de build) e bloco script{} controlado:
pipeline {
agent { label 'linux-agent' }
options {
timestamps()
timeout(time: 20, unit: 'MINUTES')
disableConcurrentBuilds()
}
tools {
jdk 'jdk-17'
maven 'maven-3.9'
}
stages {
stage('1. Code Checkout & Stability') {
steps {
checkout scm
sh 'mvn clean compile -DskipTests'
}
}
stage('2. Verificações Paralelas (Qualidade & Testes)') {
parallel {
stage('2A. Code Quality (Checkstyle)') {
steps {
sh 'mvn checkstyle:checkstyle'
}
}
stage('2B. Unit Testing') {
steps {
sh 'mvn test'
}
}
stage('2C. Dockerfile Lint (Hadolint)') {
steps {
script {
if (fileExists('Dockerfile')) {
sh 'hadolint Dockerfile --no-fail -f json > hadolint.json'
}
}
}
}
}
}
}
}Laboratório avançado · Quando usar `failFast true` dentro de um estágio `parallel`?
Quando você executa três estágios pesados em paralelo (por exemplo: Testes Unitários, Análise Estática e Varredura de Segurança) e o estágio de Testes Unitários falha logo no primeiro minuto, por padrão o Jenkins espera os outros dois estágios paralelos terminarem antes de encerrar o build. Se você adicionar failFast true (ou a opção global parallelsAlwaysFailFast()), assim que qualquer ramo paralelo falhar o Jenkins aborta os demais ramos imediatamente, economizando minutos de CPU dos seus agentes e devolvendo o alerta de erro muito mais rápido ao desenvolvedor.
Entrega da etapa
Desenhe o esqueleto do `Jenkinsfile` do seu projeto em sintaxe Declarativa: defina as opções globais (timeout, timestamps), as ferramentas do bloco tools, quais etapas rodarão em sequência e quais rodarão simultaneamente dentro de um bloco parallel.
Conexão com o livro · Capítulo 3 (Seção 3.7: Jenkins Pipeline — Scripted vs. Declarative Pipeline, Pipeline Syntax Generator e os termos Pipeline, Node, Stage, Steps e Parallel).
Cofre de Credenciais do Jenkins: Escopos, `credentials()`, `withCredentials` e Armadilhas de Interpolação
No primeiro exemplo do Capítulo 4, variáveis do SonarQube foram colocadas nas configurações globais de ambiente, mas em produção você precisa garantir que tokens do Sonar, senhas do Nexus e chaves SSH nunca apareçam em texto puro nos logs nem fiquem acessíveis para jobs de outras equipes. Como dominar o Cofre de Credenciais do Jenkins?
Tipos de Credenciais e Isolamento por Escopo (Global vs. System vs. Folder)
Nos Capítulos 3.6, 4.2 e 6.2, Sandeep Rawat mostra como cadastrar credenciais identificadas por um ID único (como git-creds, nexus-creds e snyk) para desacoplar segredos do código. O plugin Credentials e o Credentials Binding oferecem cinco formatos principais de segredos:
- Username with password: guarda usuário e senha juntos sob um único ID (usado para Git HTTPS, Sonatype Nexus e Docker Registry);
- Secret text: guarda um token único criptografado (usado para API Token do SonarQube, token do Snyk ou Webhook Token do Slack);
- SSH Username with private key: guarda a chave privada SSH (e sua passphrase opcional) para clonar repositórios Git via SSH ou executar playbooks Ansible em servidores de destino;
- Secret file: guarda um arquivo inteiro de configuração sensível (como um
kubeconfigdo Kubernetes ou um arquivosettings.xml/ certificado), disponibilizando-o em um caminho temporário apenas durante o build e apagando-o imediatamente ao final; - Escopo por Pasta (Folder-Level Credentials): em vez de cadastrar todas as credenciais no escopo Global do Jenkins (onde qualquer job do servidor poderia referenciar aquele
credentialsId), cadastre as credenciais específicas de uma squad dentro da Pasta (Folder) daquela squad! Assim, apenas os pipelines contidos naquela pasta conseguem utilizar o segredo.
`environment { credentials() }` vs. `withCredentials` e a Armadilha das Aspas Duplas em Groovy
Existem duas maneiras seguras de injetar segredos no seu Jenkinsfile, ambas ativando o mascaramento automático (`****`) no Console Output. Mas há uma armadilha de segurança em Groovy que todo engenheiro precisa conhecer:
| Método / Regra | Como funciona no `Jenkinsfile` | Cuidado Técnico Indispensável |
|---|---|---|
| 1. Helper Declarativo `credentials('id')` (No bloco environment) | Ao declarar NEXUS = credentials('nexus-creds') para um segredo do tipo Username with password, o Jenkins cria três variáveis automaticamente: $NEXUS (usuario:senha), $NEXUS_USR (apenas usuário) e $NEXUS_PSW (apenas senha)! | Prefira declarar o bloco environment apenas dentro do stage que precisa da senha, em vez do topo do pipeline inteiro. |
| 2. Bloco `withCredentials([...])` (Escopo Mínimo por Passo) | Injeta o segredo apenas dentro das chaves { ... } daquele comando específico dentro de steps, revogando a variável logo na linha seguinte. | Padrão ouro do Princípio do Menor Privilégio no tempo (visto no Capítulo 10 de Engenharia de Dados e no Capítulo 3.6 de Jenkins). |
| 3. Aspas Simples (`'...'`) vs. Aspas Duplas (`"..."`) no passo `sh` | • ERRADO (Vazamento): sh "mvn deploy -Dpass=${NEXUS_PSW}" (o Groovy interpola a senha antes de passar ao shell e emite um aviso grave de segurança!).• CORRETO (Seguro): sh 'mvn deploy -Dpass=$NEXUS_PSW' (aspas simples passam o nome da variável para o shell Linux resolver com mascaramento total). | Jamais use interpolação Groovy "${SEGREDO}" com aspas duplas para variáveis secretas! Use sempre aspas simples '$SEGREDO'. |
// Exemplo prático demonstrando as duas formas corretas de injetar credenciais no Jenkinsfile
// sem jamais usar interpolação insegura de aspas duplas Groovy:
pipeline {
agent any
stages {
stage('1. Análise SonarQube (Usando environment credentials no stage)') {
environment {
// Secret text: injeta o token apenas durante este estágio
SONAR_TOKEN = credentials('sonarqube-api-token')
}
steps {
// Aspas simples ('...') garantem que o shell leia $SONAR_TOKEN sem interpolação Groovy!
sh 'mvn sonar:sonar -Dsonar.host.url=http://sonar.interno:9000 -Dsonar.token=$SONAR_TOKEN'
}
}
stage('2. Publicação e Deploy (Usando withCredentials de escopo mínimo)') {
steps {
withCredentials([
usernamePassword(
credentialsId: 'nexus-creds',
usernameVariable: 'NEXUS_USER',
passwordVariable: 'NEXUS_PASS'
),
sshUserPrivateKey(
credentialsId: 'dev-server-ssh-key',
keyFileVariable: 'SSH_KEY_FILE',
usernameVariable: 'SSH_USER'
)
]) {
sh '''
echo "Autenticando no Nexus com usuário: $NEXUS_USER"
mvn deploy -DaltDeploymentRepository=nexus::default::http://nexus.interno:8081/repository/maven-releases/
ansible-playbook -i hosts playbook.yaml --private-key "$SSH_KEY_FILE" -u "$SSH_USER"
'''
}
}
}
}
}Laboratório avançado · Como um usuário mal-intencionado com permissão de editar um Jenkinsfile poderia burlar a máscara `****` se as credenciais forem globais?
O mascaramento do Jenkins substitui a string exata da senha por **** no log do console. Porém, se um desenvolvedor mal-intencionado tiver acesso a uma credencial Global de produção e escrever no Jenkinsfile de uma branch de teste sh 'echo $PROD_PASS | base64' ou sh 'echo $PROD_PASS | sed "s/./& /g"', a string transformada não coincidirá com o texto original e aparecerá no console! É por isso que credenciais de Produção nunca devem ficar no escopo Global aberto a qualquer branch — elas devem ficar isoladas em uma Pasta (Folder) de Deploy de Produção controlada por RBAC ou protegidas por provedores externos (como HashiCorp Vault / Cloud Secrets Manager) restritos apenas à branch main.
Entrega da etapa
Estruture o mapa de credenciais do seu pipeline no Jenkins: liste todos os segredos necessários (Git, SonarQube, Snyk, Nexus/Registry, SSH de deploy), defina o tipo e o escopo (Folder vs. Global) de cada um e escreva o trecho do `Jenkinsfile` usando withCredentials com aspas simples ('...').
Conexão com o livro · Capítulos 3 (Seção 3.6), 4 (Seções 4.1.6 e 4.2.2: variáveis SonarQube e `nexus-creds`) e 6 (Seções 6.2.1 e 6.2.5: `git-creds` e token `snyk`).
Estágios de Qualidade em Paralelo: Checkstyle, Hadolint (`Dockerfile`) e `warnings-ng`
Seu código Java/Python passa no linter da linguagem, mas o desenvolvedor alterou o Dockerfile colocando COPY . . na primeira linha, usando a tag :latest e esquecendo de limpar o cache do apt — triplicando o tamanho da imagem e o tempo de cada build. Como auditar código e contêiner juntos no Jenkins?
Integrando Checkstyle e Hadolint com `recordIssues` (Capítulos 4.1.3 e 6.2.2)
No Capítulo 4 (Seção 4.1.3) e no Capítulo 6 (Seção 6.2.2, Código 6.5), Sandeep Rawat evolui o estágio de Code Quality para auditar tanto o código-fonte da aplicação quanto a receita de containerização (Dockerfile) em paralelo, publicando ambos os relatórios no painel do Jenkins através do plugin Warnings Next Generation (`warnings-ng` / step `recordIssues`):
Na Seção 6.2.2 (Figuras 6.4 a 6.9), o livro revisa as boas práticas de escrita de Dockerfile que você já domina em profundidade no nosso curso Docker em Produção com Critério: ordenação de instruções para aproveitar cache de camadas (copiar o pom.xml e baixar dependências antes de copiar o código-fonte ./src), copiar arquivos específicos + .dockerignore, agrupar instruções RUN em multilinha, não instalar pacotes desnecessários, limpar o cache do gerenciador de pacotes (rm -rf /var/lib/apt/lists/*), usar imagens Alpine/específicas em vez de :latest e rodar com usuário de menor privilégio. Aqui no Jenkins, nosso foco é automatizar a fiscalização dessas regras a cada commit usando o Hadolint!
| Ferramenta no Estágio | Comando Executado no Agente | Integração com `recordIssues` (`warnings-ng`) |
|---|---|---|
| 1. Linter da Linguagem (ex.: Checkstyle para Java, Ruff/Flake8 para Python, ESLint para JS) | mvn checkstyle:checkstyle (gera o arquivo target/checkstyle-result.xml). | recordIssues(tools: [checkStyle(pattern: '**/checkstyle-result.xml')]) — plota a evolução histórica de avisos por commit e autor. |
| 2. Linter de Contêiner (`Hadolint`) (Haskell Dockerfile Linter) | hadolint Dockerfile --no-fail -f json | tee hadolint.json (analisa a árvore sintática do Dockerfile e valida os comandos shell internos via ShellCheck). | recordIssues(tools: [hadoLint(pattern: 'hadolint.json')]) — exibe no painel do Jenkins cada regra violada (ex.: DL3007 uso de :latest, DL3009 cache do apt). |
// Trecho completo baseado no Código 6.5 do Capítulo 6, adicionando Quality Gates
// automáticos no plugin warnings-ng para barrar commits que introduzam novos erros graves:
stage('Code Quality | Checkstyle & Hadolint') {
parallel {
stage('Checkstyle (App Code)') {
agent {
docker {
image 'maven:3.9-eclipse-temurin-17'
args '-v $HOME/.m2:/root/.m2'
reuseNode true
}
}
steps {
sh 'mvn checkstyle:checkstyle'
recordIssues(
enabledForFailure: true,
tools: [checkStyle(pattern: '**/checkstyle-result.xml')],
qualityGates: [[threshold: 1, type: 'NEW_ERROR', unstable: false]]
)
}
}
stage('Hadolint (Dockerfile Best Practices)') {
steps {
sh 'hadolint Dockerfile --no-fail -f json | tee hadolint.json'
recordIssues(
enabledForFailure: true,
tools: [hadoLint(pattern: 'hadolint.json')],
qualityGates: [[threshold: 1, type: 'TOTAL_ERROR', unstable: true]]
)
}
}
}
}Laboratório avançado · Para que serve o argumento `args '-v $HOME/.m2:/root/.m2'` quando o estágio roda dentro de um agente Docker?
Repare no detalhe prático do Código 6.5 do livro: quando um estágio sobe um contêiner efêmero do Maven (image 'maven:...') para compilar ou rodar o Checkstyle, sem mapear um volume para a pasta /root/.m2 o Maven teria que baixar centenas de megabytes de bibliotecas da internet do zero em toda execução! Ao montar o cache local do host (-v $HOME/.m2:/root/.m2) dentro do contêiner efêmero, as dependências já baixadas são reutilizadas instantaneamente, reduzindo o tempo do estágio de minutos para poucos segundos.
Entrega da etapa
Configure o estágio paralelo de Code Quality do seu `Jenkinsfile`: defina o linter da linguagem do seu projeto (Checkstyle, Ruff/Flake8, ESLint ou GolangCI-Lint) ao lado do Hadolint e estabeleça o limiar de qualityGates no recordIssues que marcará o build como instável ou reprovado.
Conexão com o livro · Capítulos 4 (Seção 4.1.3: Code Quality com Checkstyle e `warnings-ng`) e 6 (Seção 6.2.2: Boas práticas de Dockerfile, instalação do Hadolint e execução paralela no Código 6.5).
Testes Unitários (JUnit) e Cobertura de Código (Cobertura / JaCoCo) com Metas Condicionais
Como configurar o seu Jenkinsfile para executar os testes unitários em um ambiente limpo, publicar gráficos históricos de falhas e impor metas mínimas de cobertura de linha, método e condição?
Automatizando Unit Testing e Code Coverage no `Jenkinsfile` (Capítulos 4.1.4 e 6.2.3/6.2.4)
Nos Capítulos 4 e 6 (Códigos 6.6 e 6.7), Sandeep Rawat demonstra como encadear a execução dos testes unitários e a medição de cobertura de código dentro de contêineres isolados para não poluir o sistema operacional principal do agente Jenkins:
- Estágio de Unit Testing (`mvn test` / `pytest` + JUnit Parser): executa a suíte de testes unitários (localizada em
src/test/javaoutests/) que gera relatórios padronizados em XML (target/surefire-reports/*.xml). No Jenkins, o plugin JUnit /recordIssues(tools: [junitParser(...)])transforma esses arquivos XML em gráficos de tendência de testes aprovados, ignorados e quebrados a cada build; - Estágio de Code Coverage (`mvn cobertura:cobertura` / JaCoCo): instrumenta o bytecode durante a execução dos testes para gerar o relatório
coverage.xmle aplica três alvos numéricos independentes no passocobertura(ourecordCoveragemoderno):- `lineCoverageTargets: '80, 0, 0'` (meta saudável de 80% de linhas cobertas);
- `methodCoverageTargets: '80, 0, 0'` (meta saudável de 80% dos métodos testados);
- `conditionalCoverageTargets: '70, 0, 0'` (meta saudável de 70% das condições lógicas
if/else/switchexercitadas nos testes!).
| Posição no Texto `'A, B, C'` | Nome do Limiar no Plugin | Comportamento quando a cobertura fica abaixo do valor |
|---|---|---|
| 1º Número (`80`) | Healthy Target (Meta Saudável) | Acima de 80%, o indicador de saúde do projeto (ícone de sol no painel do Jenkins) fica em 100%. |
| 2º Número (`0` ou ex.: `60`) | Unhealthy Target (Meta Insalubre) | Abaixo desse percentual, o índice de saúde despenca (ícone de tempestade) alertando degradação da suíte de testes. |
| 3º Número (`0` ou ex.: `70`) | Failing / Unstable Target (Limiar de Corte) | Quando combinado com failUnstable: true ou failUnhealthy: true, reprova ou marca o build como instável automaticamente! |
// Estágios de Testes Unitários e Cobertura de Código em contêiner efêmero
// exigindo mínimo de 80% em linhas/métodos e 70% em condições (branches):
stage('Unit Testing & Code Coverage') {
agent {
docker {
image 'maven:3.9-eclipse-temurin-17'
args '-v $HOME/.m2:/root/.m2'
reuseNode true
}
}
steps {
sh 'mvn test cobertura:cobertura'
}
post {
always {
// Publica os resultados dos testes unitários mesmo se algum teste falhar:
junit allowEmptyResults: false, testResults: 'target/surefire-reports/*.xml'
// Publica o relatório de cobertura de código com metas para Linha, Método e Condição:
cobertura(
autoUpdateHealth: false,
autoUpdateStability: false,
coberturaReportFile: '**/target/site/cobertura/coverage.xml',
lineCoverageTargets: '80, 60, 75',
methodCoverageTargets: '80, 60, 75',
conditionalCoverageTargets: '70, 50, 65',
failUnhealthy: false,
failUnstable: true,
onlyStable: false,
sourceEncoding: 'UTF-8',
zoomCoverageChart: true
)
}
}
}Laboratório avançado · Por que a publicação do `junit` e da cobertura deve ficar dentro do bloco `post { always { ... } }`?
Se você colocar o comando junit 'target/surefire-reports/*.xml' na linha imediatamente abaixo de sh 'mvn test' dentro do bloco steps comum, quando um único teste unitário falhar o comando mvn test retornará código de saída diferente de zero (`exit code 1`) e o Jenkins interromperá o bloco steps naquela linha — deixando de publicar o relatório XML que mostra justamente qual teste falhou! Ao colocar a coleta do relatório dentro de post { always { ... } }, o Jenkins garante que o relatório gráfico de testes e cobertura seja sempre anexado ao build, quer os testes passem, quer falhem.
Entrega da etapa
Defina o estágio de testes e cobertura do seu `Jenkinsfile`: especifique o comando de teste unitário e a ferramenta de cobertura do seu stack (Cobertura, JaCoCo, Coverage.py/pytest-cov ou Jest/Vitest), configure os três limiares (Linha, Método e Condição) e garanta a publicação no bloco post { always }.
Conexão com o livro · Capítulos 4 (Seção 4.1.4: Unit Testing) e 6 (Seções 6.2.3 e 6.2.4: Unit Testing e Code Coverage em contêineres — Códigos 6.6 e 6.7).
Segurança DevSecOps no Pipeline: OWASP, Snyk (Container Scan), SonarQube e Simulador
Seu código próprio não tem vulnerabilidades, mas uma biblioteca transitiva no `pom.xml` ou um pacote Linux dentro da imagem base do contêiner possui uma CVE crítica recém-descoberta. Como automatizar varreduras de dependências, imagens e SonarQube no `Jenkinsfile`?
Varredura Paralela de Dependências (OWASP) e de Imagens de Contêiner (Snyk / Trivy)
Na Seção 6.2.5 (Código 6.8, páginas 129–134), Sandeep Rawat explica por que testar apenas o código da aplicação não basta quando empacotamos software em contêineres: quase sempre herdamos imagens base públicas que podem conter pacotes de sistema operacional vulneráveis ou configurações inseguras listadas nos benchmarks do CIS (Center for Internet Security) e nas bases de CVEs (Common Vulnerabilities and Exposures).
O livro compara as principais ferramentas de Image Scanning do mercado — Trivy, Anchore, Clair, AquaScanner e Snyk — e monta no Código 6.8 um estágio paralelo de segurança em duas frentes, complementado pela análise centralizada do SonarQube (Seção 4.1.6):
| Ferramenta Integrada | Alvo da Inspeção | Como funciona no Jenkinsfile |
|---|---|---|
| 1. OWASP Dependency-Check + Plugin `HTML Publisher` | Bibliotecas e dependências externas do projeto (pom.xml, package-lock.json, requirements.txt). | Executa o scanner OWASP e publica o relatório detalhado dependency-check-report.html no menu lateral do job via publishHTML([...]). |
| 2. Snyk / Trivy Container Scan + Plugin `Snyk Security` | Extrai a imagem Docker camada por camada e analisa todos os pacotes do sistema operacional e da aplicação contra CVEs e CIS. | Usa a ferramenta cadastrada no Global Tool Configuration e o token do cofre (snykTokenId: 'snyk') passando --docker imagem:${BUILD_ID}. |
| 3. SonarQube Server + Scanner + `waitForQualityGate` | Código-fonte completo: vulnerabilidades SAST (SQLi, XSS, senhas hardcoded), bugs, code smells, duplicação e cobertura. | Envia o relatório ao servidor SonarQube e aguarda o webhook de resposta do Quality Gate para aprovar ou abortar o pipeline. |
// Implementação baseada no Código 6.8 (Capítulo 6) + Seção 4.1.6 (SonarQube):
stage('Security Testing | OWASP, Snyk & SonarQube') {
parallel {
stage('1. Dependency Scan (OWASP)') {
steps {
sh 'mvn org.owasp:dependency-check-maven:check'
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'target',
reportFiles: 'dependency-check-report.html',
reportName: 'OWASP Dependency Check Report',
reportTitles: 'Relatorio de Vulnerabilidades'
])
}
}
stage('2. Container Image Scan (Snyk)') {
steps {
snykSecurity(
snykInstallation: 'snyk',
snykTokenId: 'snyk-api-token',
additionalArguments: "--docker registry.interno:5000/app/servico:${env.BUILD_ID}",
failOnError: true
)
}
}
stage('3. SonarQube Analysis & Quality Gate') {
environment {
SONAR_TOKEN = credentials('sonar-token-cred')
}
steps {
withSonarQubeEnv('sonarqube-prod') {
sh 'mvn sonar:sonar -Dsonar.token=$SONAR_TOKEN -Dsonar.java.binaries=target/classes'
}
timeout(time: 5, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
}
}Simulador de Ganho com Paralelismo no Jenkinsfile e Bloqueio DevSecOps
Estime a economia financeira mensal ao reduzir o tempo de fila dos desenvolvedores com estágios paralelos (parallel {}) e barrar vulnerabilidades (OWASP/Snyk/Sonar) antes do deploy.
Laboratório avançado · Por que os gráficos ou estilos CSS do relatório OWASP às vezes aparecem bloqueados no HTML Publisher do Jenkins?
Por segurança, o Jenkins aplica uma política rigorosa de Content Security Policy (CSP) aos arquivos estáticos servidos pelo plugin HTML Publisher para evitar que scripts maliciosos roubem a sessão do usuário logado no Jenkins. Se um relatório legítimo (como o relatório HTML do OWASP Dependency-Check ou JMeter) precisar de estilos/scripts inline, a maneira mais segura em produção é servir os relatórios estáticos através de um domínio/subdomínio dedicado de recursos (Resource Root URL configurado em Manage Jenkins > Configure System > Serve resource files from another domain), mantendo o Jenkins protegido sem relaxar a CSP global.
Entrega da etapa
Estruture o portão DevSecOps do seu `Jenkinsfile`: defina como você executará a varredura de dependências (OWASP), a varredura de camadas da imagem de contêiner (Snyk ou Trivy) e o Quality Gate do SonarQube em paralelo, e anote o resultado da sua simulação financeira acima.
Conexão com o livro · Capítulos 4 (Seções 4.1.5 e 4.1.6: Security Testing com OWASP Dependency-Check, `publishHTML` e SonarQube) e 6 (Seção 6.2.5: Image Scanning Tools — Trivy, Anchore, Clair, Aqua, Snyk e Código 6.8).
Operações Intermediárias: Upload de Artefatos (Nexus / Registry) e DB Versioning (Flyway)
Por que recompilar o código a cada ambiente (Dev, QA, Produção) viola a confiabilidade da entrega contínua, e como garantir que o artefato binário publicado no Nexus (ou a imagem no Registry) suba sincronizado com as migrações do banco de dados no Flyway?
Publicando Artefatos Imutáveis no Sonatype Nexus e no Docker Registry (Seção 4.2)
Na Seção 4.2 (páginas 89–98), Sandeep Rawat conecta os estágios de pré-deploy aos estágios intermediários que preparam o terreno para o deploy: Artifact Generation & Upload e DB Update. A regra fundamental é construir o pacote binário (.war, .jar, pacote Python/Node ou imagem Docker) uma única vez e promovê-lo entre os ambientes a partir de um repositório central de artefatos:
- Por que usar o plugin `nexusArtifactUploader` no Jenkinsfile em vez de fixar o Nexus no `pom.xml`? Na página 92, o autor destaca um detalhe arquitetural elegante: se você colocar o endereço do servidor Nexus dentro do
pom.xmlde 30 repositórios e o endereço ou porta do Nexus mudar, você terá que editar 30 arquivos de código-fonte! Ao usar o stepnexusArtifactUploaderdo Jenkins (comcredentialsId: 'nexus-creds'), toda a configuração do repositório fica centralizada no pipeline; - E quando o artefato é uma Imagem Docker (Capítulo 6)? Em aplicações containerizadas (cujo processo de build multi-stage estudamos no curso Docker em Produção com Critério), o estágio de upload autentica no Registro privado (Nexus Docker Registry, Harbor, ECR, GHCR) usando
docker.withRegistry('https://...', 'registry-creds')e publica a imagem marcada com o número imutável do build (${env.BUILD_ID}).
Database as Code: Versionando o Schema com Flyway no Pipeline (Seção 2.3.2 e 4.2.4)
Uma das maiores causas de queda em deploys é atualizar o código da aplicação esquecendo de aplicar uma alteração de tabela (ALTER TABLE) no banco de dados daquele ambiente. Com o Flyway (ou Liquibase), cada alteração de schema é um script SQL numerado no Git (ex.: db/migration/V1__create_users.sql, V2__add_phone_column.sql). O Flyway mantém uma tabela de metadados (flyway_schema_history) dentro do próprio banco: quando o estágio DB Update roda mvn flyway:migrate no Jenkins, o Flyway verifica os checksums dos scripts já aplicados e executa automaticamente apenas as migrações novas pendentes!
| Etapa Intermediária | Ferramenta / Plugin no Jenkins | Garantia Entregue ao Pipeline |
|---|---|---|
| 1. Upload de Pacote Binário | Plugin nexusArtifactUploader (Sonatype Nexus 3) ou JFrog Artifactory. | Guarda o artefato compilado com versão exata (ex.: v1.${BUILD_NUMBER}) para deploy e rollback instantâneo sem recompilar. |
| 2. Push de Imagem Contêiner | Plugin docker-workflow (docker.withRegistry + image.push()). | Publica a imagem que já passou pelo Hadolint e pelo Snyk para ser consumida pelos ambientes de QA e Produção. |
| 3. Migração de Banco (DB Update) | Flyway (flyway:migrate) ou Liquibase. | Garante paridade total de schema entre Dev, QA, UAT e Produção com histórico auditável de versão. |
// Estágios intermediários baseados na Seção 4.2 (Nexus + Ansible Dev + Flyway DB Update):
stage('Uploading Artifact to Nexus') {
steps {
nexusArtifactUploader(
nexusVersion: 'nexus3',
protocol: 'http',
nexusUrl: 'nexus.interno:8081',
groupId: 'org.atelie',
version: "1.0.${env.BUILD_NUMBER}",
repository: 'spring3hibernate-releases',
credentialsId: 'nexus-creds',
artifacts: [[
artifactId: 'spring3hibernate',
classifier: '',
file: 'target/Spring3HibernateApp.war',
type: 'war'
]]
)
}
}
stage('Database Migration (Flyway DB as Code)') {
steps {
withCredentials([usernamePassword(credentialsId: 'db-dev-creds', usernameVariable: 'DB_USER', passwordVariable: 'DB_PASS')]) {
// Valida se nenhum script antigo foi adulterado (validate) antes de aplicar os novos (migrate):
sh 'mvn flyway:validate flyway:migrate -Dflyway.user=$DB_USER -Dflyway.password=$DB_PASS'
}
}
}Laboratório avançado · Como projetar migrações Flyway que não quebrem a versão anterior caso você precise fazer rollback da aplicação?
Se a versão v1.1 da sua aplicação inclui uma migração Flyway que renomeia ou apaga uma coluna que a versão v1.0 ainda lê (ALTER TABLE DROP COLUMN), e cinco minutos após o deploy você precisar voltar o binário da aplicação para a v1.0, a versão v1.0 quebrará porque a coluna sumiu! Por isso, aplique o padrão Expand and Contract (Expandir e Contrair): na v1.1, apenas adicione a coluna nova mantendo a antiga compatível; só remova a coluna antiga em uma migração futura quando tiver certeza de que nenhum rollback para a v1.0 será necessário.
Entrega da etapa
Defina as operações intermediárias do seu `Jenkinsfile`: especifique como os artefatos compilados ou imagens Docker serão versionados e enviados ao repositório central (Nexus / Artifactory / Registry) e como as alterações de banco de dados serão validadas e aplicadas via Flyway ou Liquibase.
Conexão com o livro · Capítulos 2 (Seção 2.3: Artifact Management e DB Versioning) e 4 (Seção 4.2: CI Pipeline update with Intermediate steps — Generating Artifacts, Uploading Artifacts to Nexus, Deployment to Dev e DB Update com Flyway).
Multibranch Pipelines, Condicionais `when` e Notificações Inteligentes (`post`, Slack e Email)
Como usar um único Jenkinsfile para que branches `feature/*` rodem apenas os testes e validações, enquanto a branch `develop` publica o artefato e atualiza o ambiente de Dev — avisando a equipe no Slack e por e-mail com o diagnóstico exato do build?
Operando Multibranch Pipelines com Condicionais `when` (Seção 4.1.7)
Nas páginas 87–89 do Capítulo 4 (Figuras 4.19 a 4.23), Sandeep Rawat converte o job tradicional em um Multibranch Pipeline. Como o mesmo arquivo Jenkinsfile viaja junto com o código por todas as branches (feature/*, develop, main) e Pull Requests, usamos o bloco declarativo when { ... } para controlar quais estágios executam em cada contexto:
- Os estágios de Pre-Deployment Checks (Checkout, Code Stability, Checkstyle/Hadolint, Unit Tests, Cobertura, OWASP, Snyk e SonarQube) rodam em todas as branches e Pull Requests para impedir que código quebrado seja mesclado;
- Os estágios de Upload no Nexus, Deploy em Dev e Migração Flyway levam a cláusula
when { branch 'develop' }(ouwhen { anyOf { branch 'develop'; branch 'main' } }), executando apenas após o merge aprovado!
Notificações Inteligentes com o Bloco `post`, Slack e Email-ext (Seção 4.3)
Na Seção 4.3 (páginas 98–100), o autor implementa a estratégia de Notificações Inteligentes planejada no Capítulo 2. Em vez de criar um estágio manual de notificação que deixa de rodar se um estágio anterior falhar, na sintaxe Declarativa usamos a seção final post { ... } combinando os plugins Slack Notification (`slackSend`) e Extended Email (`emailext`):
| Bloco em `post {}` | Quando é disparado pelo Jenkins | Ação Recomendada de Notificação |
|---|---|---|
| `always` | Roda sempre ao final do pipeline, independente do status do build. | Publicar relatórios XML/HTML (`junit`, `cobertura`, `recordIssues`) e limpar o workspace temporário (`cleanWs()`). |
| `success` | Apenas quando todos os estágios terminam com sucesso (`SUCCESS`). | Enviar mensagem verde concisa no canal de builds do Slack confirmando o artefato gerado. |
| `failure` | Quando qualquer estágio falha (`FAILURE`). | Enviar alerta vermelho imediato no Slack e E-mail (`emailext`) para o autor do commit e para o Tech Lead com link direto para ${env.BUILD_URL}console. |
| `unstable` / `fixed` | `unstable`: testes/lint abaixo da meta; `fixed`: build voltou a ficar verde após uma falha anterior! | Avisar a equipe quando um pipeline quebrado foi consertado (`fixed`), reduzindo ruído desnecessário. |
// Exemplo prático de Multibranch Pipeline com filtro when{} por branch
// e notificações inteligentes no Slack e E-mail via bloco post{}:
pipeline {
agent any
stages {
stage('Pre-Deployment CI Checks') {
steps {
echo "Rodando validação completa na branch: ${env.BRANCH_NAME}"
}
}
stage('Upload Artifact & Deploy Dev') {
when {
branch 'develop'
}
steps {
echo "Branch develop detectada: enviando artefato ao Nexus e atualizando ambiente Dev..."
}
}
}
post {
success {
slackSend(
channel: '#ci-cd-builds',
color: 'good',
message: "✅ *SUCESSO:* Job `${env.JOB_NAME}` [#${env.BUILD_NUMBER}] na branch `${env.BRANCH_NAME}` concluído!\nDetalhes: ${env.BUILD_URL}"
)
}
failure {
slackSend(
channel: '#ci-cd-alertas',
color: 'danger',
message: "🚨 *FALHA no CI:* Job `${env.JOB_NAME}` [#${env.BUILD_NUMBER}] quebrou na branch `${env.BRANCH_NAME}`.\nLogs: ${env.BUILD_URL}console"
)
emailext(
to: 'techlead@empresa.com.br',
recipientProviders: [developers(), requestor()],
subject: "[FALHA CI] ${env.JOB_NAME} - Build #${env.BUILD_NUMBER}",
body: "O build #${env.BUILD_NUMBER} na branch ${env.BRANCH_NAME} falhou.\nInspecione os logs em: ${env.BUILD_URL}console"
)
}
always {
cleanWs()
}
}
}Laboratório avançado · Para que serve o `recipientProviders: [developers(), requestor()]` no plugin `emailext`?
Lembra do diálogo do Capítulo 2 (página 20) em que Abhishek e Adeel decidem que, quando um build falhar, o alerta deve ir especificamente para o desenvolvedor que fez o commit e para o líder de desenvolvimento? No plugin email-ext, o provedor developers() lê automaticamente o autor do commit Git que disparou aquele build e requestor() identifica quem acionou a execução, enviando o e-mail de falha apenas para os responsáveis diretos sem fazer spam para a empresa inteira!
Entrega da etapa
Configure o controle Multibranch e as notificações do seu `Jenkinsfile`: defina quais estágios usarão a cláusula when { branch '...' } e escreva o bloco post com as mensagens de slackSend e emailext direcionadas ao autor do commit e ao líder técnico.
Conexão com o livro · Capítulos 2 (Seção 2.5: Intelligent Notifications) e 4 (Seções 4.1.7: Converting Multibranch Pipeline e 4.3: CI Pipeline with Notification Integration — Slack e Email).
Projeto prático e caderno da Etapa 2
Consolide seu pipeline completo como código (`Jenkinsfile`), sua matriz de credenciais, seus portões de qualidade/segurança e sua Shared Library (Capítulos 3, 4 e 6 de Sandeep Rawat), valide suas decisões técnicas e exporte seu caderno antes de avançar para a Etapa 3.
Suas anotações nas 8 oficinas
Especificação do Pipeline como Código e Credenciais (Etapa 2)
Consolide a arquitetura do seu `Jenkinsfile` de produção em quatro blocos:
Checklist de domínio da Etapa 2
Marque os itens que você já validou antes de avançar para a Etapa 3:
Referências bibliográficas e conexões com o acervo (Capítulos 3, 4 e 6)
RAWAT, Sandeep. CI/CD Pipeline with Docker and Jenkins: Learn How to Build and Manage Your CI/CD Pipelines Effectively. London: BPB Publications, 2023. ISBN 978-93-55513-502 (Capítulo 3.7: Jenkins Pipeline & Shared Library; Capítulo 4: CI with Jenkins; Capítulo 6: CI with Jenkins and Docker). Documentações técnicas integradas: Jenkins Pipeline Syntax, Credentials Binding Plugin, Warnings Next Generation (`warnings-ng`), Hadolint, Cobertura, OWASP Dependency-Check, Snyk Security, SonarQube Scanner for Jenkins, Sonatype Nexus Artifact Uploader e Flyway. Cursos integrados no acervo Ateliê da Tecnologia: Docker em Produção com Critério e Trilha Git com Critério.