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
| Komponente | Beschreibung |
|---|---|
| Piper CLI | Go-Binary mit vorkonfigurierten Steps |
| Piper Library | Jenkins Shared Library für deklarative Pipelines |
| Docker Images | Build-Umgebungen für verschiedene SAP-Technologien |
| config.yml | Projekt-spezifische Konfiguration |
| Jenkinsfile | Pipeline-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
| Stage | Aktivitäten | Erfolgskriterien |
|---|---|---|
| Init | Git Checkout, Credentials laden | Repository geklont |
| Build | gCTS Pull, Objekte aktivieren | Syntaxfehler-frei |
| Test | ATC, Unit Tests, Code Coverage | Alle Tests grün |
| Deploy | Transport Release, Import | Erfolgreich importiert |
Jenkinsfile für ABAP Cloud
Das Jenkinsfile definiert die Pipeline-Struktur:
@Library('piper-lib-os') _
abapEnvironmentPipeline script: thisDiese 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 { subject: "Pipeline Failed: ${currentBuild.fullDisplayName}", body: "Check: ${env.BUILD_URL}" } }}Jenkinsfile Best Practices
| Aspekt | Empfehlung |
|---|---|
| Timeouts | Immer definieren (verhindert hängende Builds) |
| Cleanup | cleanWs() im Post-Block |
| Notifications | Mail/Slack bei Failure |
| Branch-Filter | Deploy nur auf main/release |
| Credentials | Jenkins 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 Konfigurationsteps: 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: trueATC Check-Varianten
| Variante | Beschreibung | Verwendung |
|---|---|---|
ABAP_CLOUD_DEVELOPMENT | Cloud-kompatible Prüfungen | Standard für ABAP Cloud |
ABAP_CLOUD_READINESS | Migrationsbereitschaft | On-Premise zu Cloud |
DEFAULT | Standard SAP Prüfungen | On-Premise |
CUSTOM_VARIANT | Eigene Definition | Projekt-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
| Strategie | Beschreibung | Verwendung |
|---|---|---|
| Direct Import | Sofortiger Import nach Release | Entwicklungssysteme |
| Quality Gate | Import nach manueller Freigabe | Produktivsysteme |
| Scheduled | Import zu definierten Zeiten | Nachtläufe |
| Feature Branch | Separate Transporte pro Feature | Parallele 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: trueTest-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_CLASSCLASS 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:
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
| Aspekt | Jenkins + Piper | GitHub Actions |
|---|---|---|
| Setup | Selbst gehostet | Managed Service |
| Kosten | Infrastruktur-Kosten | Pay-per-use |
| SAP-Integration | Exzellent (native) | Gut (mit Actions) |
| Flexibilität | Sehr hoch | Hoch |
| Wartung | Erforderlich | Minimal |
| Community | Groß (SAP-fokussiert) | Sehr groß |
| Secrets | Jenkins Credentials | GitHub Secrets |
| Parallelisierung | Mit Plugins | Native Matrix |
Pipeline-Erweiterungen
SAP Piper kann mit eigenen Steps erweitert werden:
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
| Problem | Ursache | Lösung |
|---|---|---|
| Timeout | Lange ATC-Prüfungen | Timeout erhöhen, Package filtern |
| Credentials | Falsche Konfiguration | Jenkins Credentials prüfen |
| gCTS Fehler | Repository nicht aktiviert | gCTS Setup verifizieren |
| ATC Fail | Kritische Findings | Code korrigieren oder Variant anpassen |
| Test Fail | Testdaten-Probleme | Test Doubles verwenden |
Debugging-Tipps
// Debug-Informationen in Pipelinepipeline { 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
| Aspekt | Empfehlung |
|---|---|
| Pipeline-Tool | SAP Piper für volle SAP-Integration |
| CI/CD-Platform | Jenkins (On-Premise) oder GitHub Actions (Cloud) |
| Quality Gates | ATC + Unit Tests + Coverage |
| Deployment | Automatisch für Dev, manuell für Prod |
| Monitoring | Pipeline-Dashboards einrichten |
| Dokumentation | Pipeline-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
- CI/CD mit ABAP Cloud - Grundlagen der CI/CD für ABAP
- gCTS: Git-enabled CTS - Git-Integration für Transportsystem
- ABAP Test Cockpit (ATC) - Code-Qualitätsprüfungen