diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 0000000..a0bf756 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,151 @@ +// Jenkins-Pipeline für den PDF KI Renamer +// Läuft auf einem Linux-Container (Synology NAS). +// Der MSI-Build ist Windows-only (jpackage + WiX Toolset 3.x). Jenkins läuft im +// Linux-Container auf Synology NAS und kann kein MSI erzeugen. Der MSI-Build +// wird bewusst manuell auf der Windows-Entwicklungsmaschine ausgeführt: +// .\mvnw.cmd clean package -P release -pl pdf-umbenenner-packaging --also-make -DskipTests + +pipeline { + agent any + + // MAJOR und MINOR werden manuell als Jenkins-Parameter gepflegt. + // BUILD_NUMBER wird automatisch durch Jenkins vergeben. + // Die resultierende Versionsnummer lautet: MAJOR.MINOR.BUILD_NUMBER + parameters { + string( + name: 'MAJOR', + defaultValue: '3', + description: 'Hauptversionsnummer (manuell pflegen)' + ) + string( + name: 'MINOR', + defaultValue: '0', + description: 'Nebenversionsnummer (manuell pflegen)' + ) + } + + environment { + // Effektive Versionsteile – übernommen aus Parametern oder State-Datei. + // Hinweis: Wenn MAJOR/MINOR aus einer persistierten State-Datei gelesen + // werden sollen (z. B. /builds/version.state), muss die Logik unten in + // der Stage 'Version bestimmen' entsprechend ergänzt werden. + // Im Minimalbetrieb werden die Parameter direkt übernommen. + EFFECTIVE_MAJOR = "${params.MAJOR}" + EFFECTIVE_MINOR = "${params.MINOR}" + } + + stages { + + stage('Checkout') { + steps { + checkout scm + } + } + + // Optionaler Stub: MAJOR/MINOR aus persistierter State-Datei laden. + // Wenn das bestehende Jenkins-Setup die Versionsnummern in einer + // State-Datei unter /builds/version.state persistiert, kann diese + // Stage die Umgebungsvariablen EFFECTIVE_MAJOR und EFFECTIVE_MINOR + // vor dem Build überschreiben. Ansonsten gelten die Parameter-Werte. + stage('Version bestimmen') { + steps { + script { + // Platzhalter: hier bei Bedarf State-Datei einlesen, + // z. B.: + // def state = readFile('/builds/version.state').trim() + // env.EFFECTIVE_MAJOR = state.split('\\.')[0] + // env.EFFECTIVE_MINOR = state.split('\\.')[1] + // + // Im Minimalbetrieb werden die Parameter-Werte verwendet: + echo "Buildversion: ${env.EFFECTIVE_MAJOR}.${env.EFFECTIVE_MINOR}.${env.BUILD_NUMBER}" + } + } + } + + stage('Maven Build') { + steps { + // -Drevision übergibt die vollständige Versionsnummer an Maven. + // Das flatten-maven-plugin im Parent-POM löst ${revision} in + // allen installierten POMs auf, sodass kein unaufgelöstes + // ${revision} in ~/.m2 verbleibt. + sh "mvn clean verify -Drevision=${env.EFFECTIVE_MAJOR}.${env.EFFECTIVE_MINOR}.${env.BUILD_NUMBER}" + } + } + + stage('Archive JAR') { + steps { + // Bash wird explizit erzwungen (#!/usr/bin/env bash, set -euo pipefail), + // weil Jenkins-Agenten standardmäßig sh (dash) verwenden, das kein + // mapfile kennt. mapfile zählt exakt die gefundenen Shade-JARs und + // bricht den Build ab, wenn nicht genau eines vorhanden ist. + sh '''#!/usr/bin/env bash +set -euo pipefail + +mapfile -t JARS < <(find pdf-umbenenner-bootstrap/target \ + -maxdepth 1 -name "pdf-umbenenner-bootstrap-*.jar" \ + ! -name "*-sources.jar" ! -name "*-javadoc.jar") + +test "${#JARS[@]}" -eq 1 \ + || { echo "FEHLER: Erwartet genau 1 Shade-JAR, gefunden: ${#JARS[@]}"; exit 1; } + +JAR_NAME="pdf-ki-renamer-${EFFECTIVE_MAJOR}.${EFFECTIVE_MINOR}.${BUILD_NUMBER}.jar" +cp "${JARS[0]}" "$JAR_NAME" +echo "Shade-JAR archiviert als: $JAR_NAME" +''' + archiveArtifacts artifacts: 'pdf-ki-renamer-*.jar', fingerprint: true + } + } + + stage('Berichte') { + steps { + // JUnit-Testergebnisse einlesen + junit testResults: '**/target/surefire-reports/*.xml', allowEmptyResults: true + + // JaCoCo-Coverage (falls im Build erzeugt) + // jacoco execPattern: '**/target/jacoco.exec' + + // PIT-Mutationstest-Bericht (falls im Build erzeugt) + // publishHTML(target: [reportDir: 'target/pit-reports', ...]) + } + } + + stage('Artefakt ablegen') { + steps { + // JAR-Kopie in zentrales Build-Verzeichnis ablegen. + // Pfad /builds/ muss auf dem Jenkins-Agent gemountet sein. + sh '''#!/usr/bin/env bash +set -euo pipefail + +BUILD_DIR="/builds/${EFFECTIVE_MAJOR}.${EFFECTIVE_MINOR}.${BUILD_NUMBER}" +mkdir -p "$BUILD_DIR" +cp pdf-ki-renamer-*.jar "$BUILD_DIR/" +echo "Artefakt abgelegt unter: $BUILD_DIR" +''' + } + } + + stage('Aufräumen') { + steps { + // Lokale JAR-Kopie im Workspace entfernen (Artefakt ist archiviert) + sh '''#!/usr/bin/env bash +set -euo pipefail +rm -f pdf-ki-renamer-*.jar +echo "Aufräumen abgeschlossen." +''' + } + } + } + + post { + success { + echo "Build ${env.EFFECTIVE_MAJOR}.${env.EFFECTIVE_MINOR}.${env.BUILD_NUMBER} erfolgreich abgeschlossen." + } + failure { + echo "Build ${env.EFFECTIVE_MAJOR}.${env.EFFECTIVE_MINOR}.${env.BUILD_NUMBER} fehlgeschlagen." + } + always { + // Workspace nach Abschluss bereinigen + cleanWs() + } + } +} diff --git a/docs/betrieb.md b/docs/betrieb.md index f7b148b..c667531 100644 --- a/docs/betrieb.md +++ b/docs/betrieb.md @@ -451,6 +451,10 @@ benötigt keine separate Java-Installation auf dem Zielsystem. Das Shade-JAR ble primäre Distributionsartefakt; der MSI ist eine zusätzliche Option für Systeme ohne Java-Installation und für den Standard-Installationspfad nach `C:\Program Files\`. +> **Hinweis zur CI-Umgebung:** Der MSI-Build ist Windows-only (`jpackage` + WiX Toolset 3.x). +> Jenkins läuft im Linux-Container auf dem Synology NAS und kann kein MSI erzeugen. +> Der MSI-Build wird bewusst manuell auf der Windows-Entwicklungsmaschine ausgeführt. + **Voraussetzungen für den Installer-Build (nur auf der Entwicklungsmaschine):** - Windows x64 - JDK 21 im PATH