Zum Inhalt

Jenkins Example Jenkinsfile

Dateiname: Jenkinsfile

Glossar zu den wichtigsten Begriffen

  • Pipeline: Definiert den gesamten CI/CD-Prozess in Jenkins.
  • Agent: Bestimmt, wo die Pipeline oder ein Stage ausgeführt wird (z.B. auf einem beliebigen verfügbaren Knoten).
    agent any
    Gibt an, dass der Job auf einem beliebigen verfügbaren Jenkins-Agent/Node läuft.
  • Environment: Definiert Umgebungsvariablen, die in der Pipeline verwendet werden.
  • Options: Konfiguriert verschiedene Optionen für die Pipeline, wie z.B. Farbausgabe, Zeitstempel, Build-Aufbewahrung und gleichzeitige Builds.
  • Parameters: Definiert Eingabeparameter, die vor dem Build gesetzt werden können (z. B. Version, Registry, Deploy Host).
  • Stages: Unterteilt die Pipeline in verschiedene Abschnitte (z.B. Build, Test, Deploy).
    • stage checkout: Holt den Quellcode aus dem Versionskontrollsystem (z.B. Git).
    • stage Build & Test (Maven): Führt den Build- und Testprozess mit Maven in einem Docker-Container aus.
    • stage Build Docker Image: Baut ein Docker-Image basierend auf dem Dockerfile im Repository.
    • stage Login & Push: Meldet sich bei der Container-Registry an und pusht das gebaute Image (optional).
    • stage Deploy (SSH → docker compose up): Führt das Deployment auf einem Remote-Host via SSH und Docker Compose
  • Steps: Einzelne Aktionen innerhalb einer Stage (z.B. Shell-Befehle ausführen).
pipeline {
  agent any

  environment {
    // --- Adjust to your registry ---
    REGISTRY_URL    = credentials('docker-registry-url')   // optional; use if your registry needs URL auth (e.g., https://ghcr.io)
    REGISTRY        = "${REGISTRY ?: ''}"                  // e.g., ghcr.io/<org> or docker.io/<user>; can be set in job params
    IMAGE_NAME      = "refcard01"                          // image/repository name
    REGISTRY_CRED   = "docker-registry-cred"               // Jenkins Credentials ID (username/password or token)
    SSH_CRED        = "deploy-ssh"                         // Jenkins Credentials ID for SSH deploy
    DEPLOY_HOST     = "${DEPLOY_HOST ?: 'your-server.example.com'}"
    DEPLOY_PATH     = "${DEPLOY_PATH ?: '/opt/refcard01'}" // remote dir with docker-compose.yml
    DOCKER_CONFIG   = "${WORKSPACE}/.docker"               // prevents polluting jenkins home
  }

  options {
    ansiColor('xterm')
    timestamps()
    buildDiscarder(logRotator(numToKeepStr: '20'))
    disableConcurrentBuilds()
  }

  parameters {
    string(name: 'REGISTRY', defaultValue: '', description: 'Registry prefix (e.g. ghcr.io/yourorg or docker.io/youruser). Leave empty for local only.')
    string(name: 'VERSION',  defaultValue: '', description: 'Optional image tag. Empty -> auto from Git describe or commit hash.')
    string(name: 'DEPLOY_HOST', defaultValue: '', description: 'Deploy target host (SSH). Leave empty to skip remote deploy.')
    string(name: 'DEPLOY_PATH', defaultValue: '', description: 'Remote deploy folder with docker-compose.yml.')
  }

  stages {

    stage('Checkout') {
      steps {
        checkout scm
        script {
          // Compute version/tag
          def tag = params.VERSION?.trim()
          if (!tag) {
            // Try git describe, fallback to short SHA + build number
            tag = sh(script: "git describe --tags --always --dirty || true", returnStdout: true).trim()
            if (!tag) {
              tag = sh(script: "git rev-parse --short HEAD", returnStdout: true).trim()
            }
            if (!tag) { tag = "build-${env.BUILD_NUMBER}" }
          }
          env.APP_VERSION = tag.replaceAll('[^a-zA-Z0-9_.-]', '-')
          echo "Using APP_VERSION: ${env.APP_VERSION}"
        }
      }
    }

    stage('Build & Test (Maven)') {
      agent {
        docker {
          image 'maven:3.9-eclipse-temurin-17'
          args '-v $HOME/.m2:/root/.m2'
          reuseNode true
        }
      }
      steps {
        sh 'mvn -B -ntp -DskipTests=false clean verify'
        stash name: 'jar', includes: 'target/*.jar'
      }
      post {
        always {
          junit testResults: '**/target/surefire-reports/*.xml', allowEmptyResults: true
        }
      }
    }

    stage('Build Docker Image') {
      when { expression { return fileExists('Dockerfile') } }
      steps {
        unstash 'jar'
        script {
          def fullImage = env.REGISTRY ? "${env.REGISTRY}/${env.IMAGE_NAME}:${env.APP_VERSION}" : "${env.IMAGE_NAME}:${env.APP_VERSION}"
          sh '''
            mkdir -p "${DOCKER_CONFIG}"
            echo "Building image: ${fullImage}"
            docker build -t "${fullImage}" .
          '''
          env.FULL_IMAGE = fullImage
        }
      }
    }

    stage('Login & Push') {
      when { expression { return env.REGISTRY?.trim() } }
      steps {
        withCredentials([usernamePassword(credentialsId: env.REGISTRY_CRED, usernameVariable: 'REG_USER', passwordVariable: 'REG_PASS')]) {
          sh '''
            mkdir -p "${DOCKER_CONFIG}"
            if [ -n "${REGISTRY_URL}" ]; then
              echo "${REG_PASS}" | docker login "${REGISTRY_URL}" -u "${REG_USER}" --password-stdin
            else
              # For Docker Hub and registries that take server from tag
              echo "${REG_PASS}" | docker login -u "${REG_USER}" --password-stdin
            fi
            docker push "${FULL_IMAGE}"
          '''
        }
      }
    }

    stage('Deploy (SSH → docker compose up)') {
      when { expression { return params.DEPLOY_HOST?.trim() } }
      steps {
        sshagent (credentials: [env.SSH_CRED]) {
          sh '''
            ssh -o StrictHostKeyChecking=no "${DEPLOY_HOST}" "mkdir -p ${DEPLOY_PATH}"
            scp -o StrictHostKeyChecking=no docker-compose.yml "${DEPLOY_HOST}:${DEPLOY_PATH}/docker-compose.yml"
            ssh -o StrictHostKeyChecking=no "${DEPLOY_HOST}" "cd ${DEPLOY_PATH} && DOCKER_IMAGE=${FULL_IMAGE} docker compose pull && DOCKER_IMAGE=${FULL_IMAGE} docker compose up -d"
          '''
        }
      }
    }
  }

  post {
    success {
      echo "Build OK. Image: ${env.FULL_IMAGE ?: '(no image)'}"
    }
    failure {
      echo "Build FAILED."
    }
  }
}