SAP Piper Pipeline für ABAP Cloud: CI/CD mit Jenkins

kategorie
DevOps
Veröffentlicht
autor
Johannes

SAP Piper ist ein Open-Source-Projekt von SAP, das vorkonfigurierte CI/CD-Pipelines für SAP-Technologien bereitstellt. Mit Piper lassen sich ABAP Cloud Projekte vollständig automatisiert bauen, testen und deployen.

SAP Piper Architektur

SAP Piper besteht aus mehreren Komponenten, die zusammenarbeiten:

┌──────────────────────────────────────────────────────────────────────────┐
│ SAP Piper Architektur │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Piper CLI │ │ Piper Library │ │ Docker Images │ │
│ │ (Go-Binary) │ │ (Shared Lib) │ │ (Build Env) │ │
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Jenkins Pipeline │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Build │ │ Test │ │ Quality │ │ Deploy │ │ │
│ │ │ Stage │ │ Stage │ │ Gate │ │ Stage │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ Konfigurationsdateien │ │
│ │ .pipeline/config.yml Jenkinsfile .pipeline/extensions/ │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────────┘

Komponenten im Detail

KomponenteBeschreibung
Piper CLIGo-Binary mit vorkonfigurierten Steps
Piper LibraryJenkins Shared Library für deklarative Pipelines
Docker ImagesBuild-Umgebungen für verschiedene SAP-Technologien
config.ymlProjekt-spezifische Konfiguration
JenkinsfilePipeline-Definition

Pipeline-Stages für ABAP Cloud

Eine typische ABAP Cloud Pipeline besteht aus mehreren Stages:

┌─────────────────────────────────────────────────────────────────────────┐
│ ABAP Cloud Pipeline Stages │
│ │
│ 1. Init 2. Build 3. Test 4. Deploy │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Checkout│ │ Pull │ │ ATC │ │ Release │ │
│ │ Config │─────▶│ gCTS │─────▶│ Unit │─────▶│ Transport│ │
│ │ Setup │ │ Activate│ │ Quality │ │ Import │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │ │
│ ▼ ▼ ▼ ▼ │
│ Credentials Syntax OK Tests Pass Deployed │
│ loaded Code active Quality OK to Target │
│ │
└─────────────────────────────────────────────────────────────────────────┘

Stage-Beschreibungen

StageAktivitätenErfolgskriterien
InitGit Checkout, Credentials ladenRepository geklont
BuildgCTS Pull, Objekte aktivierenSyntaxfehler-frei
TestATC, Unit Tests, Code CoverageAlle Tests grün
DeployTransport Release, ImportErfolgreich importiert

Jenkinsfile für ABAP Cloud

Das Jenkinsfile definiert die Pipeline-Struktur:

@Library('piper-lib-os') _
abapEnvironmentPipeline script: this

Diese minimalistische Definition nutzt die vorkonfigurierte ABAP Environment Pipeline. Für mehr Kontrolle kann die Pipeline erweitert werden:

@Library('piper-lib-os') _
pipeline {
agent any
options {
timeout(time: 2, unit: 'HOURS')
timestamps()
buildDiscarder(logRotator(numToKeepStr: '10'))
}
stages {
stage('Init') {
steps {
abapEnvironmentPipelineStageInit script: this
}
}
stage('Pull') {
steps {
abapEnvironmentPull script: this
}
}
stage('ATC Check') {
steps {
abapEnvironmentRunATCCheck script: this
}
}
stage('Unit Tests') {
steps {
abapEnvironmentRunAUnitTest script: this
}
}
stage('Quality Gate') {
steps {
script {
def atcResults = readJSON file: 'ATCResults.json'
def unitResults = readJSON file: 'AUnitResults.json'
// Prüfe ATC-Ergebnisse
if (atcResults.findings.any { it.priority == '1' }) {
error "ATC: Kritische Findings gefunden"
}
// Prüfe Unit Test Coverage
if (unitResults.coverage < 80) {
unstable "Code Coverage unter 80%"
}
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
abapEnvironmentAssemblePackages script: this
// Transport Release und Import
}
}
}
post {
always {
// Cleanup und Benachrichtigungen
cleanWs()
}
failure {
mail to: '[email protected]',
subject: "Pipeline Failed: ${currentBuild.fullDisplayName}",
body: "Check: ${env.BUILD_URL}"
}
}
}

Jenkinsfile Best Practices

AspektEmpfehlung
TimeoutsImmer definieren (verhindert hängende Builds)
CleanupcleanWs() im Post-Block
NotificationsMail/Slack bei Failure
Branch-FilterDeploy nur auf main/release
CredentialsJenkins Credentials Plugin nutzen

Konfigurationsdatei (.pipeline/config.yml)

Die zentrale Konfiguration liegt in .pipeline/config.yml:

general:
projectName: 'Z_MY_RAP_APP'
sapPlatform: 'ABAP Environment'
stages:
Init:
stepConditions:
abapEnvironmentPipelineStageInit:
configKeys:
- 'cloudFoundry/apiEndpoint'
- 'cloudFoundry/org'
- 'cloudFoundry/space'
steps:
# Cloud Foundry Konfiguration
cloudFoundry:
apiEndpoint: 'https://api.cf.eu10.hana.ondemand.com'
org: 'my-org'
space: 'dev'
credentialsId: 'cf-credentials'
# ABAP Environment Konfiguration
abapEnvironmentPull:
cfServiceInstance: 'my-abap-instance'
repositoryName: 'z_my_rap_app'
branchName: 'main'
# ATC Konfiguration
abapEnvironmentRunATCCheck:
atcConfig:
objectSet:
type: 'component'
component: 'Z_MY_RAP_APP'
checkVariant: 'ABAP_CLOUD_DEVELOPMENT'
generateHTML: true
failOnSeverity: 'error'
# Unit Test Konfiguration
abapEnvironmentRunAUnitTest:
aunitConfig:
objectSet:
type: 'component'
component: 'Z_MY_RAP_APP'
options:
withCoverage: true
coverageFormat: 'html'
failOnFailure: true
# Package Assembly
abapEnvironmentAssemblePackages:
cfServiceInstance: 'my-abap-instance'
packages:
- name: 'Z_MY_RAP_APP'
version: '1.0.0'

ATC Integration in Pipeline

Der ABAP Test Cockpit (ATC) ist zentral für die Code-Qualität:

# Erweiterte ATC Konfiguration
steps:
abapEnvironmentRunATCCheck:
atcConfig:
objectSet:
type: 'package'
component: 'Z_MY_RAP_APP'
# Check-Variante definiert Prüfungen
checkVariant: 'ABAP_CLOUD_DEVELOPMENT'
# Zusätzliche Objekt-Filter
objectFilter:
- type: 'CLAS'
namePattern: 'ZCL_*'
- type: 'TABL'
namePattern: 'Z*'
# Ausgabeoptionen
generateHTML: true
generateXML: true
# Fehlerbehandlung
failOnSeverity: 'error' # error, warning, oder info
# ATC Findings als JUnit veröffentlichen
publishResults: true

ATC Check-Varianten

VarianteBeschreibungVerwendung
ABAP_CLOUD_DEVELOPMENTCloud-kompatible PrüfungenStandard für ABAP Cloud
ABAP_CLOUD_READINESSMigrationsbereitschaftOn-Premise zu Cloud
DEFAULTStandard SAP PrüfungenOn-Premise
CUSTOM_VARIANTEigene DefinitionProjekt-spezifisch

ATC Ergebnisse auswerten

stage('ATC Check') {
steps {
abapEnvironmentRunATCCheck script: this
script {
// ATC Ergebnisse als JUnit publizieren
junit allowEmptyResults: true, testResults: 'ATCResults.xml'
// HTML Report archivieren
publishHTML target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.',
reportFiles: 'ATCResults.html',
reportName: 'ATC Report'
]
// Findings analysieren
def atcJson = readJSON file: 'ATCResults.json'
def criticalFindings = atcJson.findings.findAll { it.priority == '1' }
if (criticalFindings.size() > 0) {
currentBuild.result = 'FAILURE'
error "ATC: ${criticalFindings.size()} kritische Findings"
}
}
}
}

Automatische Transporte

Transporte können automatisch erstellt und freigegeben werden:

steps:
# Transport erstellen und Objekte aufnehmen
abapEnvironmentAssemblePackages:
cfServiceInstance: 'my-abap-instance'
packages:
- name: 'Z_MY_RAP_APP'
version: '${env.BUILD_NUMBER}'
# Transport Release
abapEnvironmentCreateSystemLocal:
cfServiceInstance: 'my-abap-instance'
transportDescription: 'CI/CD Release ${env.BUILD_NUMBER}'
# Import in Zielsystem
abapEnvironmentImport:
cfServiceInstance: 'my-abap-instance-prod'
importOption: 'transport'

Transport-Workflow in der Pipeline

stage('Transport') {
when {
allOf {
branch 'main'
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
}
}
steps {
script {
// 1. Transport erstellen
def transportId = abapEnvironmentCreateSystemLocal(
script: this,
transportDescription: "Release ${env.BUILD_NUMBER}"
)
echo "Transport erstellt: ${transportId}"
// 2. Objekte aufnehmen
abapEnvironmentAssemblePackages(
script: this,
transportId: transportId
)
// 3. Transport freigeben
abapEnvironmentReleaseTransport(
script: this,
transportId: transportId
)
// 4. In Zielsystem importieren
abapEnvironmentImport(
script: this,
transportId: transportId,
targetSystem: 'PRD'
)
}
}
}

Transport-Strategien

StrategieBeschreibungVerwendung
Direct ImportSofortiger Import nach ReleaseEntwicklungssysteme
Quality GateImport nach manueller FreigabeProduktivsysteme
ScheduledImport zu definierten ZeitenNachtläufe
Feature BranchSeparate Transporte pro FeatureParallele Entwicklung

Test-Automatisierung

ABAP Unit Tests werden automatisch ausgeführt:

steps:
abapEnvironmentRunAUnitTest:
aunitConfig:
objectSet:
type: 'package'
component: 'Z_MY_RAP_APP'
options:
# Test-Optionen
measurementSystem: true
coverageAnalyzer: true
# Coverage-Einstellungen
withCoverage: true
coverageFormat: 'html'
# Risk Level Filter
riskLevel: 'all' # all, dangerous, critical
# Duration Filter
duration: 'all' # all, short, medium, long
# Schwellenwerte
coverageThreshold:
statement: 80
branch: 70
procedure: 90
failOnFailure: true
failOnError: true

Test-Ergebnisse verarbeiten

stage('Unit Tests') {
steps {
abapEnvironmentRunAUnitTest script: this
script {
// JUnit Ergebnisse publizieren
junit allowEmptyResults: false,
testResults: 'AUnitResults.xml'
// Coverage Report
publishHTML target: [
reportDir: 'coverage',
reportFiles: 'index.html',
reportName: 'Code Coverage'
]
// Coverage Threshold prüfen
def coverage = readJSON file: 'coverage/summary.json'
if (coverage.statements.pct < 80) {
unstable "Statement Coverage: ${coverage.statements.pct}% < 80%"
}
// Test-Statistiken ausgeben
echo """
Test Results:
- Total Tests: ${coverage.tests.total}
- Passed: ${coverage.tests.passed}
- Failed: ${coverage.tests.failed}
- Coverage: ${coverage.statements.pct}%
"""
}
}
}

Testklassen-Struktur für CI/CD

"! @testing Z_MY_CLASS
CLASS ltcl_my_class DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
CLASS-DATA: environment TYPE REF TO if_osql_test_environment.
DATA: cut TYPE REF TO zcl_my_class.
CLASS-METHODS: class_setup.
CLASS-METHODS: class_teardown.
METHODS: setup.
METHODS: teardown.
METHODS: test_happy_path FOR TESTING.
METHODS: test_error_handling FOR TESTING.
ENDCLASS.
CLASS ltcl_my_class IMPLEMENTATION.
METHOD class_setup.
" Test-Double Environment einrichten
environment = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( 'ZMYTABLE' ) ) ).
ENDMETHOD.
METHOD class_teardown.
environment->destroy( ).
ENDMETHOD.
METHOD setup.
cut = NEW zcl_my_class( ).
environment->clear_doubles( ).
ENDMETHOD.
METHOD teardown.
" Cleanup
ENDMETHOD.
METHOD test_happy_path.
" Given
DATA(test_data) = VALUE zmytable( id = '001' name = 'Test' ).
environment->insert_test_data( i_data = REF #( test_data ) ).
" When
DATA(result) = cut->process( '001' ).
" Then
cl_abap_unit_assert=>assert_equals(
act = result-status
exp = 'OK'
msg = 'Status sollte OK sein' ).
ENDMETHOD.
METHOD test_error_handling.
" Given - keine Testdaten
" When/Then - Exception erwartet
TRY.
cut->process( 'INVALID' ).
cl_abap_unit_assert=>fail( 'Exception erwartet' ).
CATCH zcx_my_exception INTO DATA(lx_error).
cl_abap_unit_assert=>assert_bound( lx_error ).
ENDTRY.
ENDMETHOD.
ENDCLASS.

GitHub Actions Alternative

Neben Jenkins bietet GitHub Actions eine moderne Alternative:

.github/workflows/abap-cloud-ci.yml
name: ABAP Cloud CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
CF_API: 'https://api.cf.eu10.hana.ondemand.com'
CF_ORG: ${{ secrets.CF_ORG }}
CF_SPACE: ${{ secrets.CF_SPACE }}
ABAP_INSTANCE: ${{ secrets.ABAP_INSTANCE }}
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup CF CLI
uses: cloudfoundry/setup-cf-cli@v1
with:
cf-api: ${{ env.CF_API }}
cf-username: ${{ secrets.CF_USERNAME }}
cf-password: ${{ secrets.CF_PASSWORD }}
cf-org: ${{ env.CF_ORG }}
cf-space: ${{ env.CF_SPACE }}
- name: Login to ABAP Environment
run: |
cf login -a $CF_API -u ${{ secrets.CF_USERNAME }} \
-p ${{ secrets.CF_PASSWORD }} -o $CF_ORG -s $CF_SPACE
- name: Pull Repository
uses: SAP/abap-actions/gcts-pull@main
with:
instance: ${{ env.ABAP_INSTANCE }}
repository: ${{ github.event.repository.name }}
branch: ${{ github.ref_name }}
test:
needs: build
runs-on: ubuntu-latest
steps:
- name: Run ATC Checks
uses: SAP/abap-actions/atc-check@main
with:
instance: ${{ env.ABAP_INSTANCE }}
package: 'Z_MY_RAP_APP'
check-variant: 'ABAP_CLOUD_DEVELOPMENT'
fail-on: 'error'
- name: Run Unit Tests
uses: SAP/abap-actions/aunit@main
with:
instance: ${{ env.ABAP_INSTANCE }}
package: 'Z_MY_RAP_APP'
with-coverage: true
coverage-threshold: 80
- name: Upload Test Results
uses: actions/upload-artifact@v4
with:
name: test-results
path: |
ATCResults.xml
AUnitResults.xml
coverage/
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Create Transport
id: transport
uses: SAP/abap-actions/create-transport@main
with:
instance: ${{ env.ABAP_INSTANCE }}
description: 'GitHub Release ${{ github.run_number }}'
- name: Assemble Package
uses: SAP/abap-actions/assemble-package@main
with:
instance: ${{ env.ABAP_INSTANCE }}
transport: ${{ steps.transport.outputs.transport-id }}
package: 'Z_MY_RAP_APP'
- name: Release Transport
uses: SAP/abap-actions/release-transport@main
with:
instance: ${{ env.ABAP_INSTANCE }}
transport: ${{ steps.transport.outputs.transport-id }}

Jenkins vs. GitHub Actions

AspektJenkins + PiperGitHub Actions
SetupSelbst gehostetManaged Service
KostenInfrastruktur-KostenPay-per-use
SAP-IntegrationExzellent (native)Gut (mit Actions)
FlexibilitätSehr hochHoch
WartungErforderlichMinimal
CommunityGroß (SAP-fokussiert)Sehr groß
SecretsJenkins CredentialsGitHub Secrets
ParallelisierungMit PluginsNative Matrix

Pipeline-Erweiterungen

SAP Piper kann mit eigenen Steps erweitert werden:

.pipeline/extensions/myCustomStep.groovy
def call(script, Map parameters) {
echo "Custom Step ausgeführt"
// ABAP Cloud API aufrufen
def response = httpRequest(
url: "${parameters.abapEndpoint}/sap/opu/odata/sap/API_CUSTOM",
authentication: parameters.credentialsId,
contentType: 'APPLICATION_JSON'
)
if (response.status != 200) {
error "Custom Step fehlgeschlagen: ${response.status}"
}
return response.content
}

Extension in config.yml registrieren

steps:
myCustomStep:
abapEndpoint: 'https://my-abap-instance.abap.eu10.hana.ondemand.com'
credentialsId: 'abap-credentials'
stages:
Build:
stepConditions:
myCustomStep:
configKeys:
- 'abapEndpoint'

Fehlerbehebung

Häufige Pipeline-Probleme

ProblemUrsacheLösung
TimeoutLange ATC-PrüfungenTimeout erhöhen, Package filtern
CredentialsFalsche KonfigurationJenkins Credentials prüfen
gCTS FehlerRepository nicht aktiviertgCTS Setup verifizieren
ATC FailKritische FindingsCode korrigieren oder Variant anpassen
Test FailTestdaten-ProblemeTest Doubles verwenden

Debugging-Tipps

// Debug-Informationen in Pipeline
pipeline {
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
}
stages {
stage('Debug') {
steps {
script {
// Umgebungsvariablen ausgeben
sh 'env | sort'
// Piper Version prüfen
sh 'piper version'
// Konfiguration validieren
sh 'piper checkIfStepActive --step abapEnvironmentPull'
}
}
}
}
}

Zusammenfassung

AspektEmpfehlung
Pipeline-ToolSAP Piper für volle SAP-Integration
CI/CD-PlatformJenkins (On-Premise) oder GitHub Actions (Cloud)
Quality GatesATC + Unit Tests + Coverage
DeploymentAutomatisch für Dev, manuell für Prod
MonitoringPipeline-Dashboards einrichten
DokumentationPipeline-as-Code im Repository

SAP Piper bringt professionelle CI/CD-Praktiken in die ABAP-Welt. Mit der richtigen Konfiguration werden Builds reproduzierbar, Tests automatisiert und Deployments sicher.

Verwandte Themen