SAP Piper Pipeline pour ABAP Cloud : CI/CD avec Jenkins

Catégorie
DevOps
Publié
Auteur
Johannes

SAP Piper est un projet open source de SAP qui fournit des pipelines CI/CD préconfigurées pour les technologies SAP. Avec Piper, les projets ABAP Cloud peuvent être automatiquement construits, testés et déployés.

Architecture SAP Piper

SAP Piper se compose de plusieurs composants qui fonctionnent ensemble :

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

Composants en détail

ComposantDescription
Piper CLIBinaire Go avec étapes préconfigurées
Piper LibraryBibliothèque partagée Jenkins pour pipelines déclaratives
Docker ImagesEnvironnements de build pour différentes technologies SAP
config.ymlConfiguration spécifique au projet
JenkinsfileDéfinition de la pipeline

Étapes de pipeline pour ABAP Cloud

Une pipeline ABAP Cloud typique se compose de plusieurs étapes :

┌─────────────────────────────────────────────────────────────────────────┐
│ Étapes de Pipeline ABAP Cloud │
│ │
│ 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 │
│ chargées Code activé Qualité OK vers cible │
│ │
└─────────────────────────────────────────────────────────────────────────┘

Descriptions des étapes

ÉtapeActivitésCritères de réussite
InitGit Checkout, chargement des credentialsDépôt cloné
BuildgCTS Pull, activation des objetsSans erreurs de syntaxe
TestATC, tests unitaires, couverture de codeTous les tests au vert
DeployRelease du transport, importImporté avec succès

Jenkinsfile pour ABAP Cloud

Le Jenkinsfile définit la structure de la pipeline :

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

Cette définition minimaliste utilise la pipeline ABAP Environment préconfigurée. Pour plus de contrôle, la pipeline peut être étendue :

@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"
// Vérifier les résultats ATC
if (atcResults.findings.any { it.priority == '1' }) {
error "ATC: Findings critiques trouvés"
}
// Vérifier la couverture des tests unitaires
if (unitResults.coverage < 80) {
unstable "Couverture de code inférieure à 80%"
}
}
}
}
stage('Deploy') {
when {
branch 'main"
}
steps {
abapEnvironmentAssemblePackages script: this
// Release et import du transport
}
}
}
post {
always {
// Nettoyage et notifications
cleanWs()
}
failure {
mail to: '[email protected]',
subject: "Pipeline Failed: ${currentBuild.fullDisplayName}",
body: "Check: ${env.BUILD_URL}"
}
}
}

Bonnes pratiques Jenkinsfile

AspectRecommandation
TimeoutsToujours définir (évite les builds bloqués)
NettoyagecleanWs() dans le bloc post
NotificationsMail/Slack en cas d’échec
Filtre de branchesDéploiement uniquement sur main/release
CredentialsUtiliser le plugin Jenkins Credentials

Fichier de configuration (.pipeline/config.yml)

La configuration centrale se trouve dans .pipeline/config.yml :

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

Intégration ATC dans la pipeline

L’ABAP Test Cockpit (ATC) est central pour la qualité du code :

# Configuration ATC étendue
steps:
abapEnvironmentRunATCCheck:
atcConfig:
objectSet:
type: 'package"
component: 'Z_MY_RAP_APP"
# La variante de vérification définit les contrôles
checkVariant: 'ABAP_CLOUD_DEVELOPMENT"
# Filtres d'objets supplémentaires
objectFilter:
- type: 'CLAS"
namePattern: 'ZCL_*"
- type: 'TABL"
namePattern: 'Z*"
# Options de sortie
generateHTML: true
generateXML: true
# Gestion des erreurs
failOnSeverity: 'error' # error, warning, ou info
# Publier les findings ATC en tant que JUnit
publishResults: true

Variantes de vérification ATC

VarianteDescriptionUtilisation
ABAP_CLOUD_DEVELOPMENTVérifications compatibles CloudStandard pour ABAP Cloud
ABAP_CLOUD_READINESSPréparation à la migrationOn-Premise vers Cloud
DEFAULTVérifications SAP standardOn-Premise
CUSTOM_VARIANTDéfinition personnaliséeSpécifique au projet

Évaluer les résultats ATC

stage('ATC Check') {
steps {
abapEnvironmentRunATCCheck script: this
script {
// Publier les résultats ATC en tant que JUnit
junit allowEmptyResults: true, testResults: 'ATCResults.xml"
// Archiver le rapport HTML
publishHTML target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: '.',
reportFiles: 'ATCResults.html',
reportName: 'ATC Report"
]
// Analyser les findings
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()} findings critiques"
}
}
}
}

Transports automatiques

Les transports peuvent être créés et relâchés automatiquement :

steps:
# Créer un transport et inclure les objets
abapEnvironmentAssemblePackages:
cfServiceInstance: 'my-abap-instance"
packages:
- name: 'Z_MY_RAP_APP"
version: '${env.BUILD_NUMBER}"
# Release du transport
abapEnvironmentCreateSystemLocal:
cfServiceInstance: 'my-abap-instance"
transportDescription: 'CI/CD Release ${env.BUILD_NUMBER}"
# Import dans le système cible
abapEnvironmentImport:
cfServiceInstance: 'my-abap-instance-prod"
importOption: 'transport"

Workflow de transport dans la pipeline

stage('Transport') {
when {
allOf {
branch 'main"
expression { currentBuild.result == null || currentBuild.result == 'SUCCESS' }
}
}
steps {
script {
// 1. Créer le transport
def transportId = abapEnvironmentCreateSystemLocal(
script: this,
transportDescription: "Release ${env.BUILD_NUMBER}"
)
echo "Transport créé: ${transportId}"
// 2. Inclure les objets
abapEnvironmentAssemblePackages(
script: this,
transportId: transportId
)
// 3. Relâcher le transport
abapEnvironmentReleaseTransport(
script: this,
transportId: transportId
)
// 4. Importer dans le système cible
abapEnvironmentImport(
script: this,
transportId: transportId,
targetSystem: 'PRD"
)
}
}
}

Stratégies de transport

StratégieDescriptionUtilisation
Direct ImportImport immédiat après releaseSystèmes de développement
Quality GateImport après approbation manuelleSystèmes de production
ScheduledImport à des heures définiesExécutions nocturnes
Feature BranchTransports séparés par fonctionnalitéDéveloppement parallèle

Automatisation des tests

Les tests unitaires ABAP sont exécutés automatiquement :

steps:
abapEnvironmentRunAUnitTest:
aunitConfig:
objectSet:
type: 'package"
component: 'Z_MY_RAP_APP"
options:
# Options de test
measurementSystem: true
coverageAnalyzer: true
# Paramètres de couverture
withCoverage: true
coverageFormat: 'html"
# Filtre de niveau de risque
riskLevel: 'all' # all, dangerous, critical
# Filtre de durée
duration: 'all' # all, short, medium, long
# Seuils
coverageThreshold:
statement: 80
branch: 70
procedure: 90
failOnFailure: true
failOnError: true

Traiter les résultats des tests

stage('Unit Tests') {
steps {
abapEnvironmentRunAUnitTest script: this
script {
// Publier les résultats JUnit
junit allowEmptyResults: false,
testResults: 'AUnitResults.xml"
// Rapport de couverture
publishHTML target: [
reportDir: 'coverage',
reportFiles: 'index.html',
reportName: 'Code Coverage"
]
// Vérifier le seuil de couverture
def coverage = readJSON file: 'coverage/summary.json"
if (coverage.statements.pct < 80) {
unstable "Couverture des instructions: ${coverage.statements.pct}% < 80%"
}
// Afficher les statistiques de test
echo """
Résultats des tests:
- Total des tests: ${coverage.tests.total}
- Réussis: ${coverage.tests.passed}
- Échoués: ${coverage.tests.failed}
- Couverture: ${coverage.statements.pct}%
"""
}
}
}

Structure des classes de test pour 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.
" Configurer l'environnement Test-Double
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.
" Nettoyage
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 = 'Le statut devrait être OK' ).
ENDMETHOD.
METHOD test_error_handling.
" Given - pas de données de test
" When/Then - exception attendue
TRY.
cut->process( 'INVALID' ).
cl_abap_unit_assert=>fail( 'Exception attendue' ).
CATCH zcx_my_exception INTO DATA(lx_error).
cl_abap_unit_assert=>assert_bound( lx_error ).
ENDTRY.
ENDMETHOD.
ENDCLASS.

Alternative GitHub Actions

En plus de Jenkins, GitHub Actions offre une alternative moderne :

.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

AspectJenkins + PiperGitHub Actions
ConfigurationAuto-hébergéService géré
CoûtsCoûts d’infrastructurePaiement à l’usage
Intégration SAPExcellente (native)Bonne (avec Actions)
FlexibilitéTrès élevéeÉlevée
MaintenanceRequiseMinimale
CommunautéGrande (orientée SAP)Très grande
SecretsJenkins CredentialsGitHub Secrets
ParallélisationAvec pluginsMatrix native

Extensions de pipeline

SAP Piper peut être étendu avec des étapes personnalisées :

.pipeline/extensions/myCustomStep.groovy
def call(script, Map parameters) {
echo "Étape personnalisée exécutée"
// Appeler l'API ABAP Cloud
def response = httpRequest(
url: "${parameters.abapEndpoint}/sap/opu/odata/sap/API_CUSTOM",
authentication: parameters.credentialsId,
contentType: 'APPLICATION_JSON"
)
if (response.status != 200) {
error "Échec de l'étape personnalisée: ${response.status}"
}
return response.content
}

Enregistrer l’extension dans config.yml

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

Dépannage

Problèmes de pipeline courants

ProblèmeCauseSolution
TimeoutVérifications ATC longuesAugmenter le timeout, filtrer le package
CredentialsConfiguration incorrecteVérifier Jenkins Credentials
Erreur gCTSDépôt non activéVérifier la configuration gCTS
Échec ATCFindings critiquesCorriger le code ou adapter la variante
Échec testProblèmes de données de testUtiliser des Test Doubles

Conseils de débogage

// Informations de débogage dans la pipeline
pipeline {
options {
buildDiscarder(logRotator(numToKeepStr: '10'))
}
stages {
stage('Debug') {
steps {
script {
// Afficher les variables d'environnement
sh 'env | sort"
// Vérifier la version Piper
sh 'piper version"
// Valider la configuration
sh 'piper checkIfStepActive --step abapEnvironmentPull"
}
}
}
}
}

Résumé

AspectRecommandation
Outil de pipelineSAP Piper pour une intégration SAP complète
Plateforme CI/CDJenkins (On-Premise) ou GitHub Actions (Cloud)
Quality GatesATC + tests unitaires + couverture
DéploiementAutomatique pour Dev, manuel pour Prod
MonitoringConfigurer des tableaux de bord de pipeline
DocumentationPipeline-as-Code dans le dépôt

SAP Piper apporte des pratiques CI/CD professionnelles dans le monde ABAP. Avec la bonne configuration, les builds deviennent reproductibles, les tests automatisés et les déploiements sécurisés.

Sujets connexes