Cheatsheet Jenkins
Servidor de automação CI/CD para builds, testes e deploys
Jenkins
Pipeline
Pipeline declarativo
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'npm install'
sh 'npm run build'
}
}
stage('Test') {
steps {
sh 'npm test'
}
}
}
}
# Estructura fija y legible.
# Es el formato recomendado.El pipeline declarativo tiene estructura fija: pipeline > stages > stage > steps. Es el formato recomendado por su legibilidad. agent any define dónde ejecutar. Cada stage agrupa pasos lógicos del CI/CD.
Environment
environment {
APP_ENV = 'production'
DB_HOST = 'db.example.com'
VERSION = sh(
script: 'git rev-parse --short HEAD',
returnStdout: true).trim()
}
stages {
stage('Build') {
steps {
echo "Versión: ${env.VERSION}"
sh 'echo $APP_ENV'
}
}
}
# También puede ser por stage:
stage('Deploy') {
environment { DEPLOY_TARGET = 'aws' }
}environment define variables de entorno. sh(returnStdout: true) captura el output de comandos (ej: hash de git). Accede vía env.NOMBRE o $NOMBRE en el shell. Puede ser global o por stage.
Milestone y lock
options {
disableConcurrentBuilds()
}
stages {
stage('Deploy') {
steps {
// Lock: exclusividad sobre un recurso
lock('production-server') {
sh './deploy.sh'
}
// Milestone: ordena builds
milestone(1)
sh './notify.sh'
milestone(2)
}
}
}
# lock requiere el plugin "Lockable Resources"
# milestone aborta builds antiguosdisableConcurrentBuilds evita builds simultáneos del mismo job. lock() (plugin Lockable Resources) da exclusividad sobre un recurso compartido. milestone() garantiza el orden y aborta builds superados. Esenciales para deploys seguros.
Pipeline scriptado
node {
stage('Checkout') {
checkout scm
}
stage('Test') {
try {
sh 'npm test'
} catch (Exception e) {
echo 'Los tests fallaron'
throw e
}
}
}
# Más flexible (Groovy puro):
# - if/else, bucles, try/catch nativos
# - Sin estructura fija
# - Más difícil de mantener
#
# El declarativo cubre el 95% de los casos.El pipeline scriptado usa Groovy puro dentro de node. Es más flexible (if/else, try/catch nativos) pero menos legible. El declarativo cubre la mayoría de los casos. Usa script { } dentro del declarativo cuando necesites lógica Groovy.
Directivas principales
pipeline {
agent any // dónde ejecutar
options { } // comportamiento
parameters { } // inputs de build
environment { } // variables
tools { } // herramientas
triggers { } // disparadores
stages {
stage('X') {
when { } // condición
input { } // aprobación
steps { } // comandos
post { } // post-acciones
}
}
post { } // post-acciones global
}El declarativo tiene directivas fijas: agent, options, parameters, environment, tools, triggers, stages y post. Dentro de cada stage: when, input, steps, post.
Options
options {
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
buildDiscarder(
logRotator(numToKeepStr: '10'))
timestamps()
retry(2)
skipDefaultCheckout(true)
ansiColor('xterm')
}
# timeout: aborta tras 30 min
# disableConcurrentBuilds: 1 build a la vez
# buildDiscarder: guarda solo 10 builds
# timestamps: hora en cada línea de log
# retry: reintenta en caso de fallooptions define el comportamiento global. timeout aborta builds anchos. disableConcurrentBuilds evita ejecuciones simultáneas. buildDiscarder limita los builds guardados. timestamps y ansiColor mejoran los logs.
Tools
# Preconfigurar en:
# Manage Jenkins > Tools
tools {
nodejs 'NodeJS-20'
jdk 'JDK-17'
maven 'Maven-3.9'
gradle 'Gradle-8'
}
stages {
stage('Build') {
steps {
// node, java, mvn están en el PATH
sh 'node --version'
sh 'mvn clean package'
}
}
}
# Jenkins instala/configura
# las herramientas automáticamentetools añade herramientas al PATH del build. Configura en Manage Jenkins > Tools (nombre + versión). Soporta nodejs, jdk, maven, gradle. Jenkins puede instalarlas automáticamente.
Parámetros
parameters {
string(name: 'BRANCH',
defaultValue: 'main',
description: 'Branch a compilar')
booleanParam(name: 'DEPLOY',
defaultValue: false)
choice(name: 'ENV',
choices: ['dev', 'staging', 'prod'])
password(name: 'PASSWORD',
defaultValue: '')
}
stages {
stage('Deploy') {
steps {
echo "Branch: ${params.BRANCH}"
echo "Entorno: ${params.ENV}"
}
}
}parameters crea campos en el formulario de build. Tipos: string, booleanParam, choice, password. Accede vía params.NOMBRE. El primer build crea los campos; los siguientes muestran el formulario.
Bloque script (Groovy)
stage('Logic') {
steps {
script {
def result = sh(
script: 'cat version.txt',
returnStdout: true).trim()
if (result.startsWith('2.')) {
echo '¡Versión más nueva!'
} else {
echo 'Versión antigua'
}
['a', 'b', 'c'].each { item ->
echo "Item: ${item}"
}
}
}
}
# script { } permite Groovy completo
# dentro del pipeline declarativoscript { } abre un bloque Groovy dentro del declarativo. Permite if/else, bucles y variables (def). Esencial para lógica compleja que el declarativo no cubre. Úsalo con moderación para mantener la legibilidad.
Stages e Steps
Steps comunes
steps {
sh 'comando bash' // Linux/Mac
bat 'comando windows' // Windows
powershell 'Get-ChildItem' // PowerShell
echo "Mensaje en el log"
sleep(time: 10, unit: 'SECONDS')
// Capturar output:
def out = sh(
script: 'ls -la',
returnStdout: true)
// Fallar a propósito:
error("Algo salió mal")
}sh ejecuta bash, bat ejecuta Windows CMD, powershell ejecuta PowerShell. echo imprime. sh(returnStdout: true) captura el output. error() falla el build con un mensaje. sleep pausa.
Artefactos
stage('Build') {
steps {
sh 'npm run build'
// Guardar artefactos del build:
archiveArtifacts artifacts: 'dist/**',
fingerprint: true
}
}
post {
success {
archiveArtifacts 'build/*.jar'
}
}
// En otro job, descargar:
// copyArtifacts projectName: 'job-build',
// filter: 'dist/**'
// Los artefactos quedan en la página del buildarchiveArtifacts guarda archivos del build (binarios, informes) en Jenkins. fingerprint: true permite el rastreo. copyArtifacts (plugin) los descarga en otro job. Útil para pasar binarios entre stages de deploy.
Retry y timeout en steps
steps {
// Reintentar hasta 3 veces:
retry(3) {
sh 'npm install'
}
// Timeout solo en este paso:
timeout(time: 5, unit: 'MINUTES') {
sh './slow-tests.sh'
}
// Esperar una condición:
waitUntil {
def r = sh(script: 'curl -s http://app/health',
returnStatus: true)
return r == 0
}
}
# retry: útil para comandos inestables
# waitUntil: polling hasta el éxitoretry(n) repite un paso hasta n veces — útil para comandos inestables (npm, descargas). timeout limita un paso específico. waitUntil hace polling hasta que la condición sea verdadera (ej: esperar a que un servicio arranque).
Checkout y Git
steps {
// Checkout del SCM configurado en el job:
checkout scm
// Git explícito:
git branch: 'main',
url: 'https://github.com/user/repo'
// Con credenciales:
git branch: 'main',
url: 'git@github.com:user/repo.git',
credentialsId: 'ssh-key-id'
// Comandos git manuales:
sh 'git log --oneline -5'
sh 'git rev-parse --short HEAD'
}checkout scm usa el repositorio configurado en el job (por defecto en Multibranch). git hace checkout explícito con branch y URL. credentialsId autentica. Los comandos git manuales dan el hash del commit y el historial.
Tests e informes
stage('Tests') {
steps {
sh 'npm test -- --ci --reporters=jest-junit'
}
post {
always {
// Publicar resultados JUnit:
junit 'reports/junit.xml'
// Cobertura (plugin Cobertura):
publishHTML(target: [
reportDir: 'coverage/lcov-report',
reportFiles: 'index.html',
reportName: 'Cobertura'
])
}
}
}
# junit muestra un gráfico de tests en el job
# Siempre en post > always (aunque falle)junit publica resultados de tests (requiere XML en formato JUnit). Muestra gráficos y tendencias en el job. Publica en post > always para capturarlos incluso cuando los tests fallan. publishHTML publica informes de cobertura.
Parallel stages
stage('Tests') {
parallel {
stage('Unit') {
steps { sh 'npm run test:unit' }
}
stage('E2E') {
steps { sh 'npm run test:e2e' }
}
stage('Lint') {
steps { sh 'npm run lint' }
}
}
}
# Los 3 stages corren en simultáneo.
# Si uno falla, los otros continúan
# (failFast: false por defecto).
# Para abortar todos al primer error:
# failFast trueparallel ejecuta stages en simultáneo — ideal para tests independientes. Reduce mucho el tiempo total. Por defecto, un fallo no aborta los demás. failFast true aborta todos al primer error. Cada stage paralelo puede tener su propio agent.
Stash y unstash
stage('Build') {
steps {
sh 'npm run build'
// Guardar archivos para después:
stash name: 'build-output',
includes: 'dist/**'
}
}
stage('Deploy') {
agent { label 'prod-server' }
steps {
// Recuperar en otro agente:
unstash 'build-output'
sh './deploy.sh'
}
}
# stash/unstash comparte archivos
# entre stages/agentes diferentesstash guarda archivos temporalmente; unstash los recupera. Esencial cuando los stages corren en agentes diferentes (el workspace no se comparte). Para archivos grandes o persistentes prefiere archiveArtifacts.
Input (aprobación manual)
stage('Deploy Prod') {
input {
message "¿Hacer deploy a producción?"
ok "¡Sí, deploy!"
submitter 'admin,devops'
parameters {
string(name: 'NOTE', defaultValue: '')
}
}
steps {
echo "Nota: ${params.NOTE}"
sh './deploy.sh prod'
}
}
# El pipeline se pausa hasta que alguien apruebe.
# submitter restringe quién puede aprobar.input pausa el pipeline para aprobación manual — esencial antes de deploys a producción. submitter restringe quién puede aprobar. Puede aceptar parameters adicionales. El build queda en espera hasta que alguien haga clic en ok.
Workspace y directorios
steps {
// Directorio actual (workspace):
echo "Workspace: ${env.WORKSPACE}"
// Ejecutar en otro directorio:
dir('frontend') {
sh 'npm install'
sh 'npm run build'
}
// Directorio temporal:
ws('/tmp/build-area') {
sh 'make'
}
// Crear archivo:
writeFile file: 'version.txt',
text: "${env.BUILD_NUMBER}"
// Leer archivo:
def content = readFile('version.txt')
}dir() ejecuta pasos en otro directorio. ws() cambia el workspace. writeFile/readFile manipulan archivos sin shell. env.WORKSPACE es el directorio raíz del build. Útil para monorepos con varias carpetas.
Triggers
Cron (programación)
triggers {
// Build diario a las 2h (días laborables):
cron('H 2 * * 1-5')
// Cada 15 minutos:
cron('H/15 * * * *')
// Domingo a medianoche:
cron('H 0 * * 0')
}
# Formato: MINUTO HORA DÍA MES DÍA_SEMANA
#
# H = hash — distribuye la carga
# (evita todos los jobs a la misma hora)
#
# H/15 = cada 15 min (minuto variable)cron programa builds periódicos. Formato: MINUTO HORA DÍA MES DÍA_SEMANA. La H (hash) distribuye la carga para evitar picos. H/15 ejecuta cada 15 minutos. Ideal para builds nocturnos y tareas regulares.
Sintaxis cron de Jenkins
# MINUTO HORA DÍA MES DÍA_SEMANA H/15 * * * * # cada 15 min H 2 * * 1-5 # 2h, de lunes a viernes H 0 * * 0 # domingo a medianoche H 8 1 * * # día 1 de cada mes, 8h H */4 * * * # cada 4 horas 0 12 * * 1 # lunes al mediodía (exacto) # Campos: # MINUTO: 0-59 HORA: 0-23 # DÍA: 1-31 MES: 1-12 # DÍA_SEMANA: 0-7 (0 y 7 = domingo) # H = hash (distribuye la carga) # * = cualquier valor # 1-5 = rango */4 = paso
La sintaxis cron de Jenkins tiene 5 campos: MINUTO HORA DÍA MES DÍA_SEMANA. H distribuye la carga; usa 0 para una hora exacta. Soporta rangos (1-5) y pasos (*/4). Día de la semana: 0 y 7 son domingo.
Desactivar y silenciar
options {
// No ejecutar builds concurrentes:
disableConcurrentBuilds()
// Desactivar el job (vía UI o API):
// http://jenkins/job/NOMBRE/disable
// http://jenkins/job/NOMBRE/enable
}
// Silenciar un stage (sin log verboso):
stage('Cleanup') {
steps {
sh 'rm -rf cache/'
}
}
// Pausar builds durante mantenimiento:
// Manage Jenkins > "Quiet Down"
// (termina builds activos, no inicia nuevos)disableConcurrentBuilds evita builds simultáneos. Desactiva jobs vía UI o API (/disable). Quiet Down pone Jenkins en mantenimiento — termina los builds activos y no inicia nuevos. Útil para actualizaciones del servidor.
Poll SCM
triggers {
// Verificar cambios en Git
// cada 5 minutos:
pollSCM('H/5 * * * *')
}
# pollSCM hace un "git ls-remote" periódico.
# Solo dispara el build si hay cambios.
# Comparación:
# - cron: build siempre (aunque no haya cambios)
# - pollSCM: build solo si hay commit nuevo
# pollSCM consume recursos del servidor.
# Prefiere webhooks cuando sea posible.pollSCM verifica el repositorio periódicamente y solo dispara el build si hay cambios. Usa la misma sintaxis que cron. Consume recursos (polling). Prefiere webhooks cuando el servidor Git los soporte.
Webhook genérico
triggers {
// GitLab:
gitlab(triggerOnPush: true,
triggerOnMergeRequest: true)
// Bitbucket:
bitbucketPush()
}
# Webhook genérico (cualquier servicio):
# URL del job + /build:
# http://jenkins/job/MY-JOB/build
#
# Con token de seguridad:
# http://jenkins/job/MY-JOB/build?token=SECRET
#
# En el job: Build Triggers >
# "Trigger builds remotely"
# Authentication Token: SECRETPara GitLab usa gitlab(), para Bitbucket usa bitbucketPush(). Cualquier servicio puede disparar vía la URL /job/NOMBRE/build con un token de seguridad. Activa "Trigger builds remotely" en el job y define el token.
Webhook de GitHub
triggers {
githubPush()
}
# Configuración en GitHub:
# Repo > Settings > Webhooks > Add webhook
# Payload URL:
# http://jenkins:8080/github-webhook/
# Content type: application/json
# Events: Just the push event
# Jenkins necesita el plugin "GitHub".
# La URL termina SIEMPRE en /github-webhook/
# Ventaja sobre pollSCM:
# - Instantáneo (push, no polling)
# - Sin carga en el servidorgithubPush() dispara el build vía webhook. Configúralo en GitHub en Settings > Webhooks con la URL http://jenkins/github-webhook/. Es instantáneo (push) y no consume recursos con polling. Requiere el plugin GitHub.
Trigger con parámetros
# Disparar con parámetros vía API:
curl -X POST \
-u "user:token" \
"http://jenkins/job/DEPLOY/buildWithParameters?ENV=prod&BRANCH=main"
# En el Jenkinsfile, los parámetros
# llegan automáticamente:
parameters {
string(name: 'ENV', defaultValue: 'dev')
string(name: 'BRANCH', defaultValue: 'main')
}
stages {
stage('Deploy') {
steps {
echo "Deploy ${params.ENV} desde ${params.BRANCH}"
}
}
}Dispara builds parametrizados vía buildWithParameters en la API. Los valores llegan en params.NOMBRE. Útil para integrar con scripts externos, chatops y otras herramientas. Combínalo con un token para seguridad.
Upstream (otro job)
triggers {
// Ejecutar cuando 'parent-job' termine:
upstream(
upstreamProjects: 'parent-job',
threshold: hudson.model.Result.SUCCESS
)
}
# threshold: resultado mínimo
# - Result.SUCCESS
# - Result.UNSTABLE
# - Result.FAILURE
# Varios proyectos:
# upstreamProjects: 'job-a, job-b'
# Crea cadenas de jobs:
# build-lib -> build-app -> deployupstream dispara el job cuando otro termina. threshold define el resultado mínimo (generalmente SUCCESS). Acepta múltiples proyectos separados por comas. Permite crear cadenas de dependencia entre jobs.
Triggers de Multibranch
// En un Multibranch Pipeline, los triggers
// se configuran en la organización/carpeta,
// no en el Jenkinsfile.
// El Jenkinsfile detecta el contexto:
when {
branch 'main' // solo main
}
when {
changeRequest() // solo en PRs
}
when {
branch pattern: 'release/.*',
comparator: 'REGEXP'
}
// Multibranch crea un job por branch
// y hace un escaneo periódico del repositorio
// (o vía webhook)En un Multibranch Pipeline, Jenkins crea un job por branch/PR automáticamente. El escaneo del repositorio se configura en la carpeta. Usa when en el Jenkinsfile para controlar qué branches ejecutan. changeRequest() detecta pull requests.
Agentes e Nós
Tipos de agent
pipeline {
// Cualquier nodo disponible:
agent any
// Sin nodo global (define por stage):
agent none
// Nodo con label específica:
agent { label 'linux' }
// Dentro de un contenedor Docker:
agent {
docker { image 'node:20' }
}
// Nodo específico por nombre:
agent { node { label 'build-server-1' } }
}agent define dónde ejecuta el pipeline. any usa cualquier nodo. none obliga a definir por stage. label selecciona nodos con esa etiqueta. docker ejecuta dentro de un contenedor. Es obligatorio en el pipeline o en cada stage.
Añadir un agente SSH
# Manage Jenkins > Nodes > New Node # Nombre: build-linux-1 # Type: Permanent Agent # Configuración: # - Remote root directory: /home/jenkins # - Labels: linux docker # - Launch method: # "Launch agents via SSH" # - Host: 192.168.1.50 # - Credentials: clave SSH o user/pass # Jenkins instala el agente # automáticamente vía SSH y lo conecta. # Verificar la conexión en: # Manage Jenkins > Nodes > (nodo) > Log
Añade agentes en Manage Jenkins > Nodes > New Node. Define el directorio raíz remoto, las labels y el método de lanzamiento (SSH para Linux). Jenkins instala y conecta el agente automáticamente. Verifica el estado en el log del nodo.
Dockerfile agent
// Construir y usar una imagen
// a partir de un Dockerfile del repo:
agent {
dockerfile {
filename 'Dockerfile.ci'
dir 'docker'
additionalBuildArgs '--build-arg VERSION=2'
}
}
stages {
stage('Build') {
steps {
// Entorno definido en el Dockerfile
sh 'my-tool --version'
}
}
}
// Jenkins hace el build de la imagen
// y ejecuta el pipeline dentro de ella.dockerfile construye una imagen a partir de un Dockerfile del repositorio y ejecuta el pipeline dentro de ella. filename y dir localizan el archivo. Permite entornos de build totalmente personalizados y versionados.
Docker agent
agent {
docker {
image 'node:20-alpine'
args '-v /tmp/cache:/root/.npm'
reuseNode true
registryUrl 'https://registry.example.com'
registryCredentialsId 'registry-creds'
}
}
stages {
stage('Build') {
steps {
// node/npm ya están instalados
sh 'node --version'
sh 'npm ci'
}
}
}El docker agent ejecuta el build dentro de un contenedor — sin instalar herramientas en el servidor. args pasa opciones (ej: volúmenes para caché). reuseNode reutiliza el contenedor entre stages. registryUrl usa registros privados.
Workspace y customWorkspace
agent {
node {
label 'linux'
customWorkspace '/opt/builds/my-project'
}
}
stages {
stage('Build') {
steps {
// Workspace por defecto:
// /home/jenkins/workspace/NOMBRE-JOB
echo "En: ${env.WORKSPACE}"
}
}
}
# customWorkspace define un directorio
# fijo en vez del workspace automático.
# Útil cuando la ruta es importante
# (ej: licencias, configs absolutas).El workspace por defecto es ~/workspace/NOMBRE-JOB. customWorkspace define un directorio fijo — útil cuando la ruta absoluta importa. Requiere agent { node { } }. El workspace se comparte entre stages del mismo agente.
Agent por stage
pipeline {
agent none
stages {
stage('Build Frontend') {
agent { docker { image 'node:20' } }
steps { sh 'npm run build' }
}
stage('Build Backend') {
agent { docker { image 'maven:3.9' } }
steps { sh 'mvn package' }
}
stage('Deploy') {
agent { label 'prod-server' }
steps { sh './deploy.sh' }
}
}
}Con agent none en la parte superior, cada stage define su propio agente. Permite usar herramientas diferentes por etapa (Node para frontend, Maven para backend). El deploy puede correr en un servidor específico vía label.
Ejecutores (executors)
# Cada nodo tiene N executors — # el número de builds en paralelo. # Configurar: # Manage Jenkins > Nodes > (nodo) > Configure # "# of executors": 4 # Reglas prácticas: # - Servidor de builds: 1-2 por CPU core # - Builds pesados: menos executors # - Builds ligeros: más executors # Si todos los executors están ocupados, # los builds quedan en cola (Build Queue). # Monitorizar en: # Manage Jenkins > Nodes # (columna "Build Queue")
Los executors definen cuántos builds corren en paralelo en un nodo. Configura por nodo (1-2 por core es razonable). Los builds pesados piden menos executors. Cuando todos están ocupados, los builds quedan en la Build Queue. Monitoriza en Nodes.
Labels y nodos
# Configurar labels:
# Manage Jenkins > Nodes > (nodo) > Configure
# Labels: linux docker build
# Expresiones de label:
agent { label 'linux && docker' } // ambos
agent { label 'linux || mac' } // cualquiera
agent { label '!windows' } // excepto
# Los agentes pueden ser:
# - El propio servidor (built-in node)
# - Máquinas SSH (Linux/Mac)
# - Máquinas Windows (agente JNLP)
# - Contenedores Kubernetes (plugin)
# Ve los nodos en:
# Manage Jenkins > NodesLas labels etiquetan nodos para su selección (linux, docker). Soporta expresiones: &&, ||, !. Los agentes pueden ser máquinas SSH, Windows (JNLP) o pods de Kubernetes. Gestiónalos en Manage Jenkins > Nodes.
Agentes Kubernetes
// Plugin: Kubernetes
// Crea pods efímeros por build
agent {
kubernetes {
yaml '''
apiVersion: v1
kind: Pod
spec:
containers:
- name: node
image: node:20
command: ['sleep', 'infinity']
'''
}
}
stages {
stage('Build') {
steps {
container('node') {
sh 'npm ci && npm test'
}
}
}
}El plugin Kubernetes crea pods efímeros para cada build — escala automáticamente. Define el pod en YAML con los contenedores necesarios. container() selecciona el contenedor. Los pods se destruyen tras el build. Ideal para CI a gran escala.
Variáveis e Ambiente
Variables integradas
steps {
echo "Build: ${env.BUILD_NUMBER}"
echo "Job: ${env.JOB_NAME}"
echo "URL: ${env.BUILD_URL}"
echo "Workspace: ${env.WORKSPACE}"
echo "Branch: ${env.BRANCH_NAME}"
echo "Nodo: ${env.NODE_NAME}"
echo "Jenkins: ${env.JENKINS_URL}"
}
// Resultado del build (en post/script):
// currentBuild.result
// currentBuild.currentResult
// Lista completa:
// http://jenkins/pipeline-syntax/globals#envJenkins expone variables automáticas en env: BUILD_NUMBER, JOB_NAME, BUILD_URL, WORKSPACE, BRANCH_NAME. currentBuild da el resultado. La lista completa está en pipeline-syntax/globals.
withCredentials
steps {
// Bloque que expone credenciales
// solo durante la ejecución:
withCredentials([
usernamePassword(
credentialsId: 'docker-creds',
usernameVariable: 'USER',
passwordVariable: 'PASS')
]) {
sh 'docker login -u $USER -p $PASS'
}
// Secret text:
withCredentials([
string(credentialsId: 'api-token',
variable: 'TOKEN')
]) {
sh 'curl -H "Authorization: $TOKEN" ...'
}
}withCredentials expone credenciales solo dentro del bloque — más seguro que un environment global. usernamePassword define user y pass. string para tokens. Las variables se enmascaran en el log y se limpian al final del bloque.
Archivo .env y configs
steps {
// Cargar variables de un archivo:
script {
def props = readProperties file: 'build.properties'
env.APP_VERSION = props['version']
}
// Crear un archivo de configuración:
writeFile file: '.env', text: """
APP_ENV=${params.ENV}
DB_HOST=${env.DB_HOST}
VERSION=${env.BUILD_NUMBER}
"""
sh 'cat .env'
// Limpiar al final:
// post { always { sh 'rm -f .env' } }
}
// readProperties lee archivos .propertiesreadProperties (Pipeline Utility Steps) lee archivos de configuración. writeFile genera archivos .env con valores del build. Recuerda limpiar los archivos con secretos en post > always. Patrón común para configurar aplicaciones.
Bloque environment
environment {
APP_ENV = 'production'
DB_HOST = 'db.example.com'
VERSION = '1.2.3'
// Calcular desde un comando:
GIT_HASH = sh(
script: 'git rev-parse --short HEAD',
returnStdout: true).trim()
// Usar una credencial:
DB_PASS = credentials('db-password')
}
stage('Build') {
environment {
BUILD_TARGET = 'release' // solo en este stage
}
steps {
sh 'echo $APP_ENV $GIT_HASH'
}
}environment define variables globales o por stage. sh(returnStdout: true) captura el output de comandos. credentials() inyecta secretos con seguridad. En el shell accede vía $NOMBRE; en Groovy vía env.NOMBRE.
Variables de build dinámicas
steps {
script {
// Definir una variable dinámica:
env.TIMESTAMP = sh(
script: 'date +%Y%m%d%H%M%S',
returnStdout: true).trim()
env.IMAGE_TAG = "${env.BUILD_NUMBER}-${env.GIT_HASH}"
// Leer de un archivo:
env.VERSION = readFile('version.txt').trim()
}
echo "Tag: ${env.IMAGE_TAG}"
sh 'docker build -t app:$IMAGE_TAG .'
}
// env.X = ... crea variables en runtime
// (solo dentro de script { })Dentro de script { } puedes crear variables dinámicas con env.NOMBRE = valor. Útil para timestamps, tags de imagen y valores calculados. Combina sh(returnStdout) y readFile para obtener valores de comandos y archivos.
Credentials
# Crear una credencial:
# Manage Jenkins > Credentials >
# System > Global credentials > Add
# Tipos: Username/password, Secret text,
# SSH key, Certificate
environment {
DOCKER_PASS = credentials('docker-pass')
API_KEY = credentials('api-key-id')
SSH = credentials('deploy-ssh-key')
}
steps {
sh 'docker login -u user -p $DOCKER_PASS'
sh 'curl -H "X-Key: $API_KEY" https://api'
}
# Jenkins enmascara los valores en el log
# (aparecen como ****)Credentials guardan secretos con seguridad. Créalas en Manage Jenkins > Credentials. Tipos: username/password, secret text, SSH key. Inyecta con credentials(id). Jenkins enmascara los valores en los logs automáticamente.
Enmascarar secretos
steps {
// wrap que enmascara cualquier valor:
wrap([$class: 'MaskPasswordsBuildWrapper']) {
sh 'echo $MY_PASSWORD' // aparece como ****
}
// Buenas prácticas:
// 1. Nunca hagas echo de secretos
// 2. Usa credentials() / withCredentials
// 3. No pases secretos por parámetros
// de build (quedan en el historial)
// 4. Limpia archivos con secretos:
sh 'rm -f .env'
// ¡Los secretos en artefactos o logs
// quedan visibles para siempre!
}Los secretos requieren cuidado: usa credentials()/withCredentials (enmascarados automáticamente). Nunca hagas echo de contraseñas. No pases secretos por parámetros de build (quedan en el historial). Elimina los archivos .env antes de archivar.
Parámetros en steps
parameters {
string(name: 'BRANCH', defaultValue: 'main')
choice(name: 'ENV', choices: ['dev', 'prod'])
booleanParam(name: 'DEPLOY', defaultValue: false)
}
stages {
stage('Deploy') {
steps {
sh "git checkout ${params.BRANCH}"
sh "./deploy.sh ${params.ENV}"
script {
if (params.DEPLOY) {
echo 'Haciendo deploy...'
} else {
echo 'Deploy desactivado'
}
}
}
}
}Accede a los parámetros vía params.NOMBRE. En strings Groovy usa ${params.BRANCH}. Los booleanos funcionan en if. Los parámetros aparecen en el formulario "Build with Parameters". Valida los inputs antes de usarlos en comandos.
Propiedades del build
steps {
script {
// Info del build actual:
echo "Número: ${currentBuild.number}"
echo "Resultado: ${currentBuild.currentResult}"
echo "Duración: ${currentBuild.duration}"
echo "URL: ${currentBuild.absoluteUrl}"
// Causa del build:
def cause = currentBuild.getBuildCauses()
echo "Causa: ${cause}"
// Definir el resultado manualmente:
// currentBuild.result = 'UNSTABLE'
// Cambiar la descripción del build:
currentBuild.description = "Deploy ${params.ENV}"
}
}currentBuild da metadatos del build: number, currentResult, duration, getBuildCauses(). Puedes definir result manualmente (ej: UNSTABLE) y cambiar la description. Útil para informes y lógica condicional.
Condições e Pós-ações
when (condiciones)
stage('Deploy') {
when {
branch 'main'
}
steps { sh './deploy.sh' }
}
stage('Deploy PR') {
when {
changeRequest()
}
steps { echo 'Build de PR' }
}
stage('Release') {
when {
tag 'v*'
}
steps { sh './release.sh' }
}
// El stage solo se ejecuta si la condición
// es verdadera (si no, queda "skipped")when ejecuta el stage condicionalmente. Condiciones comunes: branch, tag, changeRequest() (PRs). Si es falso, el stage aparece como "skipped". Esencial para pipelines que se comportan diferente por branch.
Notificaciones de Slack
post {
failure {
slackSend channel: '#dev',
color: 'danger',
message: "FALLÓ: ${env.JOB_NAME} #${env.BUILD_NUMBER} - ${env.BUILD_URL}"
}
success {
slackSend channel: '#dev',
color: 'good',
message: "OK: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
}
}
# Configurar:
# Manage Jenkins > System >
# Slack > Workspace + Token
# Se necesita el plugin "Slack Notification"slackSend envía mensajes a Slack. Configura el workspace y el token en Manage Jenkins > System. Usa color (good/danger) e incluye BUILD_URL para un enlace directo. Ponlo en post para notificar por resultado.
Resultados del build
steps {
script {
// Marcar como inestable
// (no falla el pipeline):
currentBuild.result = 'UNSTABLE'
// Fallar con un mensaje:
// error('Algo salió mal')
}
}
// Significado de los resultados:
// SUCCESS - todo salió bien
// UNSTABLE - los tests fallaron, pero
// el build "pasó"
// FAILURE - algo falló
// ABORTED - cancelado manualmente
// unstable es típico cuando los tests
// fallan pero quieres continuar el deployResultados: SUCCESS, UNSTABLE, FAILURE, ABORTED. UNSTABLE indica tests fallidos sin bloquear — útil para continuar el pipeline. Defínelo con currentBuild.result. error() fuerza FAILURE.
when con expresiones
stage('Deploy') {
when {
expression { params.DEPLOY == true }
}
steps { sh './deploy.sh' }
}
stage('Prod') {
when {
allOf {
branch 'main'
expression { params.ENV == 'prod' }
}
}
steps { sh './deploy-prod.sh' }
}
stage('Tests') {
when {
not { branch 'main' }
}
steps { sh 'npm test' }
}
// allOf = Y, anyOf = O, not = negaciónexpression { } permite condiciones Groovy arbitrarias. Combina con allOf (Y), anyOf (O) y not (negación). expression da flexibilidad total — compara parámetros, variables y cualquier lógica.
Notificaciones por email
post {
failure {
emailext(
subject: "FALLÓ: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """El build falló.
Ver: ${env.BUILD_URL}""",
to: 'dev@company.com',
attachLog: true
)
}
success {
emailext subject: 'Build OK',
to: 'dev@company.com'
}
}
# Configurar SMTP:
# Manage Jenkins > System > E-mail
# Plugin: "Email Extension"emailext (plugin Email Extension) envía emails ricos. Configura SMTP en Manage Jenkins > System. attachLog adjunta el log del build. Soporta plantillas HTML, adjuntos y listas de destinatarios. Notifica en post > failure.
when con cambios
// Solo ejecutar si ciertos archivos
// cambiaron desde el último build:
stage('Backend') {
when {
changeset "src/main/**"
}
steps { sh 'mvn package' }
}
stage('Frontend') {
when {
changeset "frontend/**"
}
steps { sh 'npm run build' }
}
// Comparar con una branch:
stage('Deploy') {
when {
branch pattern: "release/.*",
comparator: "REGEXP"
}
steps { sh './deploy.sh' }
}changeset ejecuta el stage solo si cambiaron archivos específicos — ideal para monorepos (build solo de lo que cambió). branch pattern con REGEXP hace matching avanzado. Reduce el tiempo de build en repositorios grandes.
Retry y timeout
options {
// Timeout global del pipeline:
timeout(time: 1, unit: 'HOURS')
// Repetir el pipeline en caso de fallo:
retry(2)
}
stages {
stage('Install') {
steps {
// Retry solo en este paso:
retry(3) {
sh 'npm install'
}
}
}
stage('Tests anchos') {
options {
timeout(time: 20, unit: 'MINUTES')
}
steps { sh 'npm run test:e2e' }
}
}timeout aborta builds que exceden el tiempo (global o por stage). retry repite en caso de fallo (útil para pasos inestables). Ambos pueden aplicarse en options globales, de stage, o como steps. Previenen builds atascados.
Post actions
pipeline {
agent any
stages { /* ... */ }
post {
success {
echo '¡Build OK!'
}
failure {
echo '¡Build falló!'
}
unstable {
echo 'Los tests fallaron'
}
always {
echo 'Siempre se ejecuta'
cleanWs()
}
aborted {
echo 'Fue cancelado'
}
}
}post define acciones por resultado del build. Condiciones: success, failure, unstable, aborted, always. always se ejecuta siempre (ideal para limpieza). Puede ser global o por stage.
Limpieza (cleanWs)
post {
always {
// Limpiar el workspace:
cleanWs()
// O borrar solo ciertos patrones:
cleanWs(patterns: [
[pattern: 'node_modules/**', type: 'INCLUDE'],
[pattern: '.git/**', type: 'EXCLUDE']
])
}
}
// Alternativa manual:
// sh 'rm -rf ${env.WORKSPACE}/*'
// cleanWs() libera espacio en disco.
// Recomendado en agentes con poco espacio.cleanWs() limpia el workspace después del build — libera espacio en disco. Ponlo en post > always. Acepta patterns para incluir/excluir rutas. Esencial en agentes con almacenamiento limitado o builds grandes.
Docker e Avançado
Build y push de Docker
stage('Docker') {
steps {
script {
def img = docker.build("myapp:${env.BUILD_NUMBER}")
docker.withRegistry('https://registry.example.com', 'registry-creds') {
img.push()
img.push('latest')
}
}
}
}
// docker.build() construye la imagen
// withRegistry autentica en el registry
// push() envía con tag específica y latestdocker.build() construye la imagen. docker.withRegistry() autentica en un registry privado usando credenciales. img.push() envía — hace push con el BUILD_NUMBER y con latest. Patrón clásico de CI para contenedores.
Pipeline completo (ejemplo)
pipeline {
agent { docker { image 'node:20' } }
options {
timeout(time: 30, unit: 'MINUTES')
disableConcurrentBuilds()
}
stages {
stage('Install') {
steps { sh 'npm ci' }
}
stage('Test') {
steps { sh 'npm test' }
post { always { junit 'reports/*.xml' } }
}
stage('Build') {
steps { sh 'npm run build' }
}
stage('Deploy') {
when { branch 'main' }
steps { sh './deploy.sh' }
}
}
post {
failure { slackSend channel: '#dev', color: 'danger',
message: "Falló: ${env.JOB_NAME}" }
always { cleanWs() }
}
}Ejemplo completo: docker agent, options de timeout y concurrencia, stages de install/test/build/deploy, when para hacer deploy solo en main, junit para informes, notificación de Slack y cleanWs. Una buena plantilla inicial.
Buenas prácticas
// 1. Jenkinsfile en el repo (Pipeline as Code)
// 2. Builds rápidos (< 10 min) — usa caché
// 3. Tests temprano (fail fast)
// 4. Stages paralelos cuando sea posible
// 5. Nunca guardar secretos en el Jenkinsfile
// 6. cleanWs() para liberar espacio
// 7. timeout en todo (evita builds atascados)
// 8. Notificar solo en fallos (evita ruido)
// 9. Usar docker agents (entornos limpios)
// 10. Versionar y revisar el Jenkinsfile
// Caché de dependencias:
// agent { docker {
// image 'node:20'
// args '-v cache-npm:/root/.npm'
// }}Buenas prácticas: Pipeline as Code, builds rápidos con caché, tests temprano (fail fast), stages paralelos, nunca guardar secretos en el Jenkinsfile, cleanWs(), timeout en todo, notificar solo en fallos, docker agents para entornos limpios.
Docker Compose
stage('Tests de integración') {
steps {
// Levantar el stack de dependencias:
sh 'docker compose up -d db redis'
// Esperar a que la BD esté lista:
sh 'sleep 10'
// Ejecutar tests:
sh 'docker compose exec -T web npm test'
// Limpiar:
sh 'docker compose down -v'
}
}
// -T en exec (sin TTY, necesario en CI)
// down -v elimina volúmenes
// Siempre limpiar en post > alwaysdocker compose levanta dependencias (BD, Redis) para tests de integración. Usa -T en exec (sin TTY en CI). Limpia con down -v en post > always. Permite tests realistas sin instalar servicios en el agente.
Debug y logs
steps {
// Ver variables de entorno:
sh 'printenv'
// Verificar herramientas:
sh 'node --version && npm --version'
sh 'which docker'
// Logs con color (plugin AnsiColor):
// options { ansiColor('xterm') }
// Ver el pipeline generado:
// Job > "Pipeline Syntax" >
// "Declarative Directive Generator"
// Replay: editar y re-ejecutar
// un build fallido (Job > Build > Replay)
// Validar el Jenkinsfile:
// Job > "Pipeline Syntax" >
// "Validate Pipeline"
}Para debug: printenv muestra las variables, verifica versiones de herramientas. Replay permite editar y re-ejecutar un build fallido. El Declarative Directive Generator genera snippets. Valida el Jenkinsfile antes de commitear.
Shared Libraries
// Repo de la lib: vars/deploy.groovy
def call(String environment) {
echo "Deploy a ${environment}"
sh "./deploy.sh ${environment}"
}
// Jenkinsfile:
@Library('my-lib') _
pipeline {
agent any
stages {
stage('Deploy') {
steps {
deploy('production')
}
}
}
}
// Configurar:
// Manage Jenkins > System >
// Global Pipeline LibrariesShared Libraries permiten reutilizar código entre pipelines. Crea archivos vars/nombre.groovy en un repo separado. Importa con @Library y llama como función. Configura en Global Pipeline Libraries. Elimina la duplicación de lógica de deploy.
Seguridad del pipeline
// 1. Revisar cambios en el Jenkinsfile (PR) // antes del merge — ¡es código! // 2. Proteger branches: // properties([ // [$class: 'BranchProtectionProperty'] // ]) // 3. No ejecutar código de PRs // sin sandbox: // Manage Jenkins > Security > // "Script Security" // 4. Credenciales con scope limitado: // (Job-specific en vez de Global) // 5. Principio del menor privilegio: // los agentes de build no deben tener // acceso a producción // 6. Auditar con el plugin Audit Trail
Seguridad: trata el Jenkinsfile como código (revísalo en PRs). Usa Script Security para sandbox de código no confiable. Credenciales con scope por job. Los agentes de build no deben acceder a producción. Audita con Audit Trail.
Multibranch Pipeline
# New Item > Multibranch Pipeline
# Branch Sources: GitHub/GitLab/Bitbucket
# (con credenciales)
# Jenkins:
# 1. Escanea el repositorio
# 2. Crea un job por branch con Jenkinsfile
# 3. Crea jobs para Pull Requests
# 4. Elimina jobs de branches borradas
# En el Jenkinsfile, detecta el contexto:
when { branch 'main' } # solo main
when { changeRequest() } # solo PRs
when { tag 'v*' } # solo tags
# Escaneo automático + webhooksMultibranch Pipeline crea automáticamente un job por branch y PR que tenga Jenkinsfile. Hace escaneo periódico y reacciona a webhooks. Elimina jobs de branches borradas. Usa when para diferenciar el comportamiento. El patrón moderno de CI.
Backup de Jenkins
# Todo está en JENKINS_HOME: # - Docker: volumen jenkins_home # - Linux: /var/lib/jenkins # Backup simple (parar el servicio): sudo systemctl stop jenkins tar -czf jenkins-backup-$(date +%F).tar.gz \ /var/lib/jenkins sudo systemctl start jenkins # O copiar solo lo esencial: # - jobs/ (configuraciones de los jobs) # - config.xml (config global) # - secrets/ y credentials.xml # - users/ # Plugins: lista en # Manage Jenkins > Plugins > Installed # Restore: extraer en JENKINS_HOME # y reiniciar el servicio
Todo el estado está en JENKINS_HOME (/var/lib/jenkins). Haz backup con el servicio parado (tar del directorio). Lo esencial: jobs/, config.xml, credentials.xml, secrets/. Restaura extrayendo y reiniciando.
Instalação e Setup
Instalar con Docker
# Forma más simple — Docker: docker run -d \ --name jenkins \ -p 8080:8080 \ -p 50000:50000 \ -v jenkins_home:/var/jenkins_home \ jenkins/jenkins:lts # Acceder: # http://localhost:8080 # Obtener la contraseña inicial: docker exec jenkins \ cat /var/jenkins_home/secrets/initialAdminPassword # El volumen jenkins_home persiste # toda la configuración y los jobs
La forma más fácil es vía Docker con la imagen jenkins/jenkins:lts. El puerto 8080 es la interfaz web y 50000 es para agentes. El volumen jenkins_home lo persiste todo. La contraseña inicial está en secrets/initialAdminPassword.
Jenkinsfile en el repositorio
# Buena práctica: Pipeline as Code # Archivo "Jenkinsfile" en la raíz del repo # En el job de Jenkins: # Definition: Pipeline script from SCM # SCM: Git # Repository URL: https://github.com/user/repo # Script Path: Jenkinsfile # Ventajas: # - Versionado con el código # - Revisado en pull requests # - Historial de cambios # - Única fuente de verdad # Jenkins hace checkout automático # y ejecuta el Jenkinsfile del branch
La buena práctica es Pipeline as Code — guardar el pipeline en un Jenkinsfile en la raíz del repositorio. En el job usa Pipeline script from SCM. Queda versionado, revisado en PRs y es la única fuente de verdad. Jenkins hace el checkout automáticamente.
Estructura de un job
# Anatomía de un Pipeline job: # # General: # - Description, GitHub project # Build Triggers: # - Cuándo ejecutar (cron, webhook) # Pipeline: # - Definition, Script/SCM # Post-build Actions: # - Notificaciones, artefactos # Tipos de item: # - Freestyle project (legado, simple) # - Pipeline (recomendado) # - Multibranch Pipeline (por branch) # - Folder (organizar jobs) # - Organization Folder (GitHub org) # Organiza los jobs en Folders por equipo
Un job tiene secciones: General, Build Triggers, Pipeline y Post-build. Prefiere Pipeline sobre Freestyle. Multibranch crea jobs por branch automáticamente. Organiza con Folders por equipo o proyecto.
Instalar en Linux
# Ubuntu/Debian: # 1. Instalar Java (requerido): sudo apt install openjdk-17-jre # 2. Añadir el repositorio de Jenkins: curl -fsSL https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key \ | sudo tee /usr/share/keyrings/jenkins-keyring.asc echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \ https://pkg.jenkins.io/debian-stable binary/" \ | sudo tee /etc/apt/sources.list.d/jenkins.list # 3. Instalar: sudo apt update sudo apt install jenkins # 4. Iniciar: sudo systemctl start jenkins sudo systemctl enable jenkins
Jenkins requiere Java (JRE 17+). Añade el repositorio oficial con la clave GPG. systemctl gestiona el servicio. La configuración queda en /var/lib/jenkins. Prefiere el canal debian-stable (LTS).
Gestionar plugins
# Manage Jenkins > Plugins # Pestañas: # - Updates: actualizaciones disponibles # - Available: instalar nuevos # - Installed: gestionar existentes # Plugins populares: # - Git, GitHub, GitLab # - Docker Pipeline # - Pipeline Utility Steps # - Slack Notification # - Email Extension # - JUnit, Cobertura (tests) # - AnsiColor (logs con color) # Manage Jenkins > Plugins > Available # (puede ser necesario reiniciar)
Los plugins extienden Jenkins. Gestiónalos en Manage Jenkins > Plugins. Esenciales: Git, Docker Pipeline, Pipeline Utility Steps. Para notificaciones: Slack y Email Extension. Mantén los plugins actualizados por seguridad.
Setup inicial (wizard)
# 1. Acceder a http://localhost:8080 # 2. Introducir la contraseña inicial: sudo cat /var/lib/jenkins/secrets/initialAdminPassword # 3. Instalar los plugins sugeridos # (o seleccionar manualmente) # 4. Crear un usuario admin # 5. Configurar la URL de Jenkins # Plugins esenciales tras la instalación: # - Pipeline # - Git # - Docker Pipeline # - Credentials Binding # - Blue Ocean (UI moderna, opcional)
El wizard inicial pide la contraseña de initialAdminPassword. Instala los plugins sugeridos. Crea un usuario admin en vez de usar el admin por defecto. Los plugins Pipeline, Git y Docker Pipeline son esenciales para CI/CD.
Usuarios y seguridad
# Manage Jenkins > Security # Realm (autenticación): # - Jenkins users (interno) # - LDAP / Active Directory # - GitHub OAuth # Authorization (permisos): # - Logged-in users can do anything # - Matrix-based security (recomendado) # - Project-based Matrix # Crear usuario: # Manage Jenkins > Users > Create User # Buenas prácticas: # - Nunca usar admin para builds # - Crear tokens de API en vez de contraseñas # - Auditar con el plugin Audit Trail
Configura la seguridad en Manage Jenkins > Security. El Realm define la autenticación (interno, LDAP, OAuth). La Matrix-based security da control fino de permisos. Usa tokens de API en vez de contraseñas para automatización.
Crear el primer Pipeline
# 1. New Item > Pipeline > OK
# 2. En "Pipeline":
# Definition: Pipeline script
# 3. Pegar en el editor:
pipeline {
agent any
stages {
stage('Hola') {
steps {
echo '¡Hola, Jenkins!'
}
}
}
}
# 4. Save > Build Now
# 5. Ver el output en "Console Output"Crea un Pipeline en New Item. Empieza con un Pipeline script pegado en el editor. agent any ejecuta en cualquier nodo. echo imprime en el log. Build Now ejecuta y Console Output muestra el resultado.
Tokens de API
# Generar token: # Usuario > Configure > API Token > Add new Token # Disparar un build vía API: curl -X POST \ -u "user:API_TOKEN" \ "http://jenkins:8080/job/MY-JOB/build" # Con parámetros: curl -X POST \ -u "user:API_TOKEN" \ "http://jenkins:8080/job/MY-JOB/buildWithParameters?BRANCH=main" # Ver estado del build (JSON): curl -u "user:API_TOKEN" \ "http://jenkins:8080/job/MY-JOB/lastBuild/api/json"
Los tokens de API sustituyen contraseñas en automatización. Genéralos por usuario en Configure. Dispara builds con POST /job/NOMBRE/build. buildWithParameters acepta parámetros. La API devuelve JSON con el estado del build.