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
| Composant | Description |
|---|---|
| Piper CLI | Binaire Go avec étapes préconfigurées |
| Piper Library | Bibliothèque partagée Jenkins pour pipelines déclaratives |
| Docker Images | Environnements de build pour différentes technologies SAP |
| config.yml | Configuration spécifique au projet |
| Jenkinsfile | Dé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
| Étape | Activités | Critères de réussite |
|---|---|---|
| Init | Git Checkout, chargement des credentials | Dépôt cloné |
| Build | gCTS Pull, activation des objets | Sans erreurs de syntaxe |
| Test | ATC, tests unitaires, couverture de code | Tous les tests au vert |
| Deploy | Release du transport, import | Importé avec succès |
Jenkinsfile pour ABAP Cloud
Le Jenkinsfile définit la structure de la pipeline :
@Library('piper-lib-os') _
abapEnvironmentPipeline script: thisCette 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 { subject: "Pipeline Failed: ${currentBuild.fullDisplayName}", body: "Check: ${env.BUILD_URL}" } }}Bonnes pratiques Jenkinsfile
| Aspect | Recommandation |
|---|---|
| Timeouts | Toujours définir (évite les builds bloqués) |
| Nettoyage | cleanWs() dans le bloc post |
| Notifications | Mail/Slack en cas d’échec |
| Filtre de branches | Déploiement uniquement sur main/release |
| Credentials | Utiliser 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 étenduesteps: 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: trueVariantes de vérification ATC
| Variante | Description | Utilisation |
|---|---|---|
ABAP_CLOUD_DEVELOPMENT | Vérifications compatibles Cloud | Standard pour ABAP Cloud |
ABAP_CLOUD_READINESS | Préparation à la migration | On-Premise vers Cloud |
DEFAULT | Vérifications SAP standard | On-Premise |
CUSTOM_VARIANT | Définition personnalisée | Spé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égie | Description | Utilisation |
|---|---|---|
| Direct Import | Import immédiat après release | Systèmes de développement |
| Quality Gate | Import après approbation manuelle | Systèmes de production |
| Scheduled | Import à des heures définies | Exécutions nocturnes |
| Feature Branch | Transports 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: trueTraiter 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_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. " 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 :
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
| Aspect | Jenkins + Piper | GitHub Actions |
|---|---|---|
| Configuration | Auto-hébergé | Service géré |
| Coûts | Coûts d’infrastructure | Paiement à l’usage |
| Intégration SAP | Excellente (native) | Bonne (avec Actions) |
| Flexibilité | Très élevée | Élevée |
| Maintenance | Requise | Minimale |
| Communauté | Grande (orientée SAP) | Très grande |
| Secrets | Jenkins Credentials | GitHub Secrets |
| Parallélisation | Avec plugins | Matrix native |
Extensions de pipeline
SAP Piper peut être étendu avec des étapes personnalisées :
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ème | Cause | Solution |
|---|---|---|
| Timeout | Vérifications ATC longues | Augmenter le timeout, filtrer le package |
| Credentials | Configuration incorrecte | Vérifier Jenkins Credentials |
| Erreur gCTS | Dépôt non activé | Vérifier la configuration gCTS |
| Échec ATC | Findings critiques | Corriger le code ou adapter la variante |
| Échec test | Problèmes de données de test | Utiliser des Test Doubles |
Conseils de débogage
// Informations de débogage dans la pipelinepipeline { 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é
| Aspect | Recommandation |
|---|---|
| Outil de pipeline | SAP Piper pour une intégration SAP complète |
| Plateforme CI/CD | Jenkins (On-Premise) ou GitHub Actions (Cloud) |
| Quality Gates | ATC + tests unitaires + couverture |
| Déploiement | Automatique pour Dev, manuel pour Prod |
| Monitoring | Configurer des tableaux de bord de pipeline |
| Documentation | Pipeline-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
- CI/CD avec ABAP Cloud - Fondamentaux du CI/CD pour ABAP
- gCTS: Git-enabled CTS - Intégration Git pour le système de transport
- ABAP Test Cockpit (ATC) - Vérifications de la qualité du code