gCTS & CI/CD in ABAP Cloud: DevOps-Pipeline einrichten

kategorie
DevOps
Veröffentlicht
autor
Johannes

gCTS (Git-enabled Change and Transport System) und CI/CD-Pipelines bringen moderne DevOps-Praktiken in die ABAP-Welt. In diesem Artikel lernst du, wie du eine vollständige DevOps-Pipeline für ABAP Cloud einrichtest - von der Git-Integration bis zur automatisierten Deployment-Pipeline.

Was ist gCTS?

gCTS verbindet das klassische SAP-Transportwesen mit Git-Repositories. Statt Transportaufträge manuell zwischen Systemen zu verschieben, werden ABAP-Objekte als Dateien in Git gespeichert und über Git-Operationen synchronisiert.

Kernkonzept

┌─────────────────────────────────────────────────────────────────────┐
│ Entwicklungslandschaft mit gCTS │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ DEV │ │ QAS │ │ PRD │ │
│ │ System │ │ System │ │ System │ │
│ │ ┌───────┐ │ │ ┌───────┐ │ │ ┌───────┐ │ │
│ │ │ gCTS │ │ │ │ gCTS │ │ │ │ gCTS │ │ │
│ │ │Client │ │ │ │Client │ │ │ │Client │ │ │
│ │ └───┬───┘ │ │ └───┬───┘ │ │ └───┬───┘ │ │
│ └──────│──────┘ └──────│──────┘ └──────│──────┘ │
│ │ │ │ │
│ └─────────┬────────┴────────┬─────────┘ │
│ Push │ │ Pull │
│ ▼ ▼ │
│ ┌───────────────────────────────┐ │
│ │ Git Repository │ │
│ │ (GitHub / GitLab / Azure) │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘

Klassisches CTS vs. gCTS

AspektKlassisches CTSgCTS
VersionierungTransportaufträgeGit-Commits
BranchingNicht möglichVollständiges Branching
VergleichEingeschränktDiff zwischen Commits
CollaborationLimitiertPull Requests, Code Review
HistoryTransportlogsVollständige Git-Historie
RollbackManuellGit Revert/Reset
CI/CDSchwierigNative Integration
ToolingSAP-spezifischStandard Git-Tools

gCTS vs. abapGit: Wann nutze ich was?

Eine häufige Frage in der ABAP-Community: Was ist der Unterschied zwischen gCTS und abapGit?

KriteriumgCTSabapGit
HerkunftSAP-Produkt (offiziell)Open-Source (Community)
ZielgruppeIT Operations & EnterpriseEntwickler & Teams
FokusTransport-AutomatisierungCode-Versionierung
Git-IntegrationPush & PullVollständig (bidirektional)
KostenSAP-Lizenz erforderlichKostenlos
Setup-KomplexitätHöher (Enterprise-Setup)Geringer (Quick Start)
CI/CD PipelineNative SAP IntegrationManuell einrichtbar
Transport-IntegrationVollständig integriertKeine
SAP-SupportJa, offizieller SupportCommunity-Support

Empfehlung

  • gCTS wählen: Enterprise-Umgebungen mit produktiven Systemlandschaften, wenn offizieller SAP-Support benötigt wird
  • abapGit wählen: Open-Source-Projekte, Entwicklerteams, schneller Einstieg, maximale Flexibilität
  • Kombination: Beide Tools können kombiniert werden - abapGit für Entwicklung, gCTS für Enterprise-Deployments

Für einen detaillierten Einstieg in abapGit, siehe den abapGit Tutorial-Artikel.


CI/CD-Konzepte für ABAP

Continuous Integration (CI) und Continuous Delivery (CD) bringen bewährte DevOps-Praktiken in die ABAP-Entwicklung:

PhaseOhne CI/CDMit CI/CD
TestsManuell, oft vergessenAutomatisch bei jedem Commit
Code-QualitätSporadische ATC-ChecksKontinuierliche Prüfung
DeploymentManueller TransportAutomatisiert & reproduzierbar
FeedbackVerzögert, nach TagenSofort, in Minuten
RisikoGroße Releases, hohes RisikoKleine Änderungen, geringes Risiko
RollbackKomplex, manuellEinfach, automatisiert

Die CI/CD-Pipeline für ABAP

┌─────────────────────────────────────────────────────────────────────────┐
│ CI/CD Pipeline für ABAP Cloud │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Code │ │ Build │ │ Test │ │ Quality │ │ Deploy │ │
│ │ Push │──▶│ Check │──▶│ Stage │──▶│ Gate │──▶│ Stage │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
│ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ │
│ Git Push Syntax & ABAP Unit ATC Checks gCTS Pull │
│ Trigger Activation Tests Code Review Transport │
│ Integration Coverage │
│ Tests Security │
└─────────────────────────────────────────────────────────────────────────┘

gCTS einrichten: Setup-Anleitung

Voraussetzungen

  • SAP S/4HANA oder SAP BTP ABAP Environment
  • Git-Repository (GitHub, GitLab, Azure DevOps)
  • SSL-Zertifikate für Git-Server
  • Berechtigungen: S_CTS_ADMI, S_CTS_SADM

Schritt 1: SSL-Zertifikate importieren

Transaktion: STRUST
1. SSL Client (Standard) öffnen
2. Zertifikat importieren:
- GitHub: github.com
- GitLab: gitlab.com (oder eigene Domain)
3. Zertifikatsliste aktualisieren
4. Speichern

Schritt 2: gCTS-Repository anlegen

In der Transaktion GCTS_MAINT oder über die Fiori App:

┌──────────────────────────────────────────────────────────────┐
│ Repository Configuration │
├──────────────────────────────────────────────────────────────┤
│ Repository Name: Z_MY_PROJECT │
│ Remote URL: https://github.com/company/project.git │
│ Branch: main │
│ │
│ Authentication: │
│ ● Token-based (recommended) │
│ │
│ Username: github-service-user │
│ Token: ghp_xxxxxxxxxxxx │
│ │
│ vSID (Virtual System ID): DEV │
└──────────────────────────────────────────────────────────────┘

Schritt 3: Repository über API anlegen (optional)

CLASS zcl_gcts_setup DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS create_repository
IMPORTING
iv_repository_name TYPE string
iv_remote_url TYPE string
iv_branch TYPE string DEFAULT 'main'
RAISING
cx_web_http_client_error.
ENDCLASS.
CLASS zcl_gcts_setup IMPLEMENTATION.
METHOD create_repository.
DATA(lo_destination) = cl_http_destination_provider=>create_by_url(
i_url = |http://localhost:50000/sap/bc/cts_abapvcs/repository| ).
DATA(lo_client) = cl_web_http_client_manager=>create_by_http_destination(
i_destination = lo_destination ).
DATA: BEGIN OF ls_request,
repository TYPE string,
url TYPE string,
branch TYPE string,
role TYPE string VALUE 'SOURCE',
END OF ls_request.
ls_request-repository = iv_repository_name.
ls_request-url = iv_remote_url.
ls_request-branch = iv_branch.
DATA(lv_json) = /ui2/cl_json=>serialize(
data = ls_request
compress = abap_true
pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
DATA(lo_request) = lo_client->get_http_request( ).
lo_request->set_header_field( i_name = 'Content-Type' i_value = 'application/json' ).
lo_request->set_text( lv_json ).
DATA(lo_response) = lo_client->execute( if_web_http_client=>post ).
IF lo_response->get_status( )-code <> 201.
RAISE EXCEPTION TYPE cx_web_http_client_error.
ENDIF.
lo_client->close( ).
ENDMETHOD.
ENDCLASS.

Schritt 4: ABAP-Paket mit Repository verknüpfen

Nach dem Anlegen des Repositories muss das ABAP-Paket verknüpft werden:

" In SE80 oder über ADT:
" Paket Z_MY_PACKAGE → Properties → gCTS Repository zuweisen

GitHub Actions Pipeline für ABAP

GitHub Actions bietet maximale Flexibilität für ABAP CI/CD. Hier eine vollständige, produktionsreife Pipeline:

Vollständige Pipeline-Konfiguration

.github/workflows/abap-cicd.yml
name: ABAP CI/CD Pipeline
on:
push:
branches: [ main, develop, 'release/**' ]
pull_request:
branches: [ main, develop ]
workflow_dispatch:
inputs:
deploy_target:
description: 'Deploy to environment'
required: true
default: 'dev'
type: choice
options:
- dev
- qas
- prd
env:
ABAP_PACKAGE: ${{ vars.ABAP_PACKAGE }}
ATC_VARIANT: ${{ vars.ATC_VARIANT || 'DEFAULT' }}
jobs:
# Job 1: Syntax Check und Aktivierung
build:
name: Build & Syntax Check
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Verify ABAP Syntax
run: |
curl -X POST \
"${{ secrets.ABAP_ENDPOINT }}/sap/bc/adt/programs/checks" \
-u "${{ secrets.ABAP_USER }}:${{ secrets.ABAP_PASSWORD }}" \
-H "Content-Type: application/json" \
--fail
# Job 2: ABAP Unit Tests
unit-tests:
name: ABAP Unit Tests
needs: build
runs-on: ubuntu-latest
steps:
- name: Run ABAP Unit Tests
id: aunit
run: |
curl -X POST \
"${{ secrets.ABAP_ENDPOINT }}/sap/bc/adt/abapunit/testruns" \
-u "${{ secrets.ABAP_USER }}:${{ secrets.ABAP_PASSWORD }}" \
-H "Content-Type: application/vnd.sap.adt.abapunit.testruns.config.v4+xml" \
-H "Accept: application/vnd.sap.adt.abapunit.testruns.result.v1+xml" \
-d @- << 'EOF' > aunit_results.xml
<?xml version="1.0" encoding="UTF-8"?>
<aunit:runConfiguration xmlns:aunit="http://www.sap.com/adt/aunit">
<external>
<coverage active="true" branchCoverage="true"/>
</external>
<options>
<uriType value="semantic"/>
<testDeterminationStrategy sameProgram="true"/>
<testRiskCoverage>
<harmless active="true"/>
<dangerous active="true"/>
<critical active="true"/>
</testRiskCoverage>
<durationCoverage short="true" medium="true" long="true"/>
</options>
<adtcore:objectSets xmlns:adtcore="http://www.sap.com/adt/core">
<objectSet kind="inclusive">
<adtcore:objectReferences>
<adtcore:objectReference adtcore:uri="/sap/bc/adt/vit/wb/object_type/devck/object_name/${{ env.ABAP_PACKAGE }}"/>
</adtcore:objectReferences>
</objectSet>
</adtcore:objectSets>
</aunit:runConfiguration>
EOF
- name: Parse Test Results
run: |
if grep -q 'alerts severity="error"' aunit_results.xml; then
echo "::error::ABAP Unit Tests failed!"
exit 1
fi
echo "✅ All ABAP Unit Tests passed"
- name: Upload Test Results
uses: actions/upload-artifact@v4
with:
name: aunit-results
path: aunit_results.xml
# Job 3: ATC Code Quality Checks
atc-checks:
name: ATC Quality Checks
needs: build
runs-on: ubuntu-latest
steps:
- name: Run ATC Checks
id: atc
run: |
# ATC Run starten
RUN_ID=$(curl -X POST \
"${{ secrets.ABAP_ENDPOINT }}/sap/bc/adt/atc/runs" \
-u "${{ secrets.ABAP_USER }}:${{ secrets.ABAP_PASSWORD }}" \
-H "Content-Type: application/vnd.sap.atc.run.parameters.v1+xml" \
-d @- << EOF | grep -oP 'id="\K[^"]+' | head -1
<?xml version="1.0" encoding="UTF-8"?>
<atc:run xmlns:atc="http://www.sap.com/adt/atc">
<checkVariant>${{ env.ATC_VARIANT }}</checkVariant>
<objectSets>
<objectSet name="run">
<softwareComponent name="${{ env.ABAP_PACKAGE }}"/>
</objectSet>
</objectSets>
</atc:run>
EOF
)
echo "ATC Run ID: $RUN_ID"
sleep 30
# Ergebnisse abrufen
curl -X GET \
"${{ secrets.ABAP_ENDPOINT }}/sap/bc/adt/atc/runs/$RUN_ID/results" \
-u "${{ secrets.ABAP_USER }}:${{ secrets.ABAP_PASSWORD }}" \
-H "Accept: application/vnd.sap.atc.checkresult.v1+xml" \
> atc_results.xml
- name: Evaluate ATC Results
run: |
ERRORS=$(grep -c 'priority="1"' atc_results.xml || true)
WARNINGS=$(grep -c 'priority="2"' atc_results.xml || true)
echo "ATC Results: $ERRORS errors, $WARNINGS warnings"
if [ "$ERRORS" -gt 0 ]; then
echo "::error::ATC found $ERRORS critical issues!"
exit 1
fi
echo "✅ ATC checks passed"
- name: Upload ATC Results
uses: actions/upload-artifact@v4
with:
name: atc-results
path: atc_results.xml
# Job 4: Deploy to DEV
deploy-dev:
name: Deploy to DEV
needs: [unit-tests, atc-checks]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
environment: development
steps:
- name: Pull to DEV System via gCTS
run: |
curl -X POST \
"${{ secrets.DEV_ENDPOINT }}/sap/bc/cts_abapvcs/repository/${{ vars.GCTS_REPO }}/pull" \
-u "${{ secrets.DEPLOY_USER }}:${{ secrets.DEPLOY_PASSWORD }}" \
-H "Content-Type: application/json" \
-d '{"branch": "develop"}'
echo "✅ Deployed to DEV"
# Job 5: Deploy to QAS
deploy-qas:
name: Deploy to QAS
needs: [unit-tests, atc-checks]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: quality-assurance
steps:
- name: Pull to QAS System via gCTS
run: |
curl -X POST \
"${{ secrets.QAS_ENDPOINT }}/sap/bc/cts_abapvcs/repository/${{ vars.GCTS_REPO }}/pull" \
-u "${{ secrets.DEPLOY_USER }}:${{ secrets.DEPLOY_PASSWORD }}" \
-H "Content-Type: application/json" \
-d '{"branch": "main"}'
echo "✅ Deployed to QAS"
# Job 6: Deploy to PRD (Manual Approval)
deploy-prd:
name: Deploy to PRD
needs: deploy-qas
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'workflow_dispatch'
environment: production
steps:
- name: Pull to PRD System via gCTS
run: |
curl -X POST \
"${{ secrets.PRD_ENDPOINT }}/sap/bc/cts_abapvcs/repository/${{ vars.GCTS_REPO }}/pull" \
-u "${{ secrets.DEPLOY_USER }}:${{ secrets.DEPLOY_PASSWORD }}" \
-H "Content-Type: application/json" \
-d '{"branch": "main", "tag": "${{ github.sha }}"}'
echo "✅ Deployed to PRD"

Azure DevOps Pipeline Alternative

Für Microsoft-Umgebungen bietet Azure DevOps eine hervorragende Alternative:

azure-pipelines.yml
trigger:
branches:
include:
- main
- develop
pool:
vmImage: 'ubuntu-latest'
variables:
ABAP_PACKAGE: 'Z_MY_PACKAGE'
ATC_VARIANT: 'Z_CI_CHECKS'
stages:
- stage: Test
displayName: 'Test Stage'
jobs:
- job: ABAPUnitTests
displayName: 'ABAP Unit Tests'
steps:
- script: |
curl -X POST \
"$(ABAP_ENDPOINT)/sap/bc/adt/abapunit/testruns" \
-u "$(ABAP_USER):$(ABAP_PASSWORD)" \
-H "Content-Type: application/vnd.sap.adt.abapunit.testruns.config.v4+xml" \
-d '<aunit:runConfiguration xmlns:aunit="http://www.sap.com/adt/aunit">
<options><uriType value="semantic"/></options>
</aunit:runConfiguration>' \
--fail
displayName: 'Run ABAP Unit Tests'
- job: ATCChecks
displayName: 'ATC Quality Checks'
steps:
- script: |
curl -X POST \
"$(ABAP_ENDPOINT)/sap/bc/adt/atc/runs" \
-u "$(ABAP_USER):$(ABAP_PASSWORD)" \
-H "Content-Type: application/vnd.sap.atc.run.parameters.v1+xml" \
--fail
displayName: 'Run ATC Checks'
- stage: DeployDev
displayName: 'Deploy to DEV'
dependsOn: Test
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/develop'))
jobs:
- deployment: DeployDEV
environment: 'development'
strategy:
runOnce:
deploy:
steps:
- script: |
curl -X POST \
"$(DEV_ENDPOINT)/sap/bc/cts_abapvcs/repository/$(GCTS_REPO)/pull" \
-u "$(DEPLOY_USER):$(DEPLOY_PASSWORD)" \
-d '{"branch": "develop"}'
displayName: 'gCTS Pull to DEV'
- stage: DeployQAS
displayName: 'Deploy to QAS'
dependsOn: Test
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
jobs:
- deployment: DeployQAS
environment: 'quality-assurance'
strategy:
runOnce:
deploy:
steps:
- script: |
curl -X POST \
"$(QAS_ENDPOINT)/sap/bc/cts_abapvcs/repository/$(GCTS_REPO)/pull" \
-u "$(DEPLOY_USER):$(DEPLOY_PASSWORD)" \
-d '{"branch": "main"}'
displayName: 'gCTS Pull to QAS'

Automatisierte Tests einbinden

ABAP Unit Test Klasse für CI/CD

CLASS zcl_customer_test DEFINITION
FOR TESTING
RISK LEVEL HARMLESS
DURATION SHORT.
PRIVATE SECTION.
CLASS-DATA:
mo_environment TYPE REF TO if_cds_test_environment.
CLASS-METHODS:
class_setup,
class_teardown.
METHODS:
setup,
teardown,
test_create_customer_success FOR TESTING,
test_create_customer_invalid_email FOR TESTING,
test_update_customer_status FOR TESTING.
ENDCLASS.
CLASS zcl_customer_test IMPLEMENTATION.
METHOD class_setup.
" CDS Test Double Framework für isolierte Tests
mo_environment = cl_cds_test_environment=>create_for_multiple_cds(
VALUE #(
( i_for_entity = 'ZI_CUSTOMER' )
( i_for_entity = 'ZI_ORDER' )
)
).
ENDMETHOD.
METHOD class_teardown.
mo_environment->destroy( ).
ENDMETHOD.
METHOD setup.
mo_environment->clear_doubles( ).
" Mock-Daten einfügen
DATA(lt_customers) = VALUE zt_customer_t(
( customer_id = '1' name = 'Test GmbH' email = '[email protected]' status = 'A' )
).
mo_environment->insert_test_data( lt_customers ).
ENDMETHOD.
METHOD teardown.
ROLLBACK WORK.
ENDMETHOD.
METHOD test_create_customer_success.
" Given
DATA(lo_cut) = NEW zcl_customer_service( ).
DATA(ls_input) = VALUE zs_customer_create(
name = 'New Customer'
).
" When
DATA(ls_result) = lo_cut->create_customer( ls_input ).
" Then
cl_abap_unit_assert=>assert_not_initial(
act = ls_result-customer_id
msg = 'Customer ID should be generated'
).
ENDMETHOD.
METHOD test_create_customer_invalid_email.
" Given
DATA(lo_cut) = NEW zcl_customer_service( ).
DATA(ls_input) = VALUE zs_customer_create(
name = 'Invalid Customer'
email = 'not-an-email'
).
" When / Then
TRY.
lo_cut->create_customer( ls_input ).
cl_abap_unit_assert=>fail( 'Exception expected' ).
CATCH zcx_customer_error INTO DATA(lx_error).
cl_abap_unit_assert=>assert_equals(
act = lx_error->error_code
exp = 'INVALID_EMAIL'
).
ENDTRY.
ENDMETHOD.
METHOD test_update_customer_status.
" Given
DATA(lo_cut) = NEW zcl_customer_service( ).
" When
lo_cut->set_customer_status( iv_customer_id = '1' iv_status = 'I' ).
" Then
DATA(ls_customer) = lo_cut->get_customer( '1' ).
cl_abap_unit_assert=>assert_equals(
act = ls_customer-status
exp = 'I'
msg = 'Status should be updated'
).
ENDMETHOD.
ENDCLASS.

Test-Annotations für CI/CD-Reporting

"! <p class="shorttext">Customer Service Test Coverage</p>
"! @testing ZCL_CUSTOMER_SERVICE
CLASS zcl_customer_test DEFINITION
FOR TESTING
RISK LEVEL HARMLESS " Wichtig: HARMLESS für CI/CD
DURATION SHORT. " Wichtig: SHORT für schnelle Pipelines

Code Quality Checks: ATC-Variante für CI/CD

Custom ATC-Variante erstellen

Erstelle eine eigene ATC-Variante für CI/CD-Prüfungen:

┌────────────────────────────────────────────────────────────────┐
│ ATC Check Variant: Z_CICD_CHECKS │
├────────────────────────────────────────────────────────────────┤
│ │
│ Priority 1 (Blocker - Pipeline fails): │
│ ☑ Syntax Errors │
│ ☑ Security Vulnerabilities (SQL Injection, XSS) │
│ ☑ Performance Critical (SELECT without WHERE) │
│ ☑ Obsolete Statements (not cloud-ready) │
│ │
│ Priority 2 (Warning - Report only): │
│ ☑ Naming Conventions │
│ ☑ Code Style │
│ ☑ Missing Documentation │
│ ☑ Unused Variables │
│ │
│ Priority 3 (Info - Ignored in CI/CD): │
│ ☐ Spelling Suggestions │
│ ☐ Refactoring Opportunities │
│ │
│ Exemptions: │
│ - Test Classes (RISK LEVEL, DURATION checks) │
│ - Generated Code (SICF handlers) │
└────────────────────────────────────────────────────────────────┘

ATC-Exemption im Code

METHOD call_legacy_function.
" ATC Exemption für bewusst genutzten Legacy-Code
"#EC CI_NOCHECK - Legacy-Integration, Migration geplant Q3
CALL FUNCTION 'Z_LEGACY_FUNCTION'
EXPORTING
iv_input = mv_input.
ENDMETHOD.

Branching-Strategie für ABAP

Empfohlenes GitFlow-Modell

main (Production)
├── release/1.0 ────────────────────────────► PRD
│ │
├── develop ─────────────────────────────────► QAS
│ │
│ ├── feature/US-001-kunde-anlegen ────────► DEV
│ │ │
│ │ └── Merge via Pull Request
│ │
│ └── feature/US-002-material-suche ───────► DEV
└── hotfix/fix-calculation ──────────────────► Merge to main & develop

Branch-Regeln

BranchZweckDeployment
mainStabile, getestete ReleasesPRD
developIntegration aller FeaturesQAS
feature/*Ein Branch pro User StoryDEV
bugfix/*Schnelle FehlerbehebungenDEV
release/*Release-KandidatenQAS → PRD
hotfix/*Kritische ProduktionsfixesPRD (direkt)

Notifications und Reporting

Slack-Integration in GitHub Actions

# Am Ende der Pipeline hinzufügen
- name: Notify Slack on Failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
channel-id: 'C01234567'
slack-message: |
:x: *ABAP Pipeline Failed*
Repository: ${{ github.repository }}
Branch: ${{ github.ref_name }}
Commit: ${{ github.sha }}
<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Details>
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
- name: Notify Slack on Success
if: success()
uses: slackapi/slack-github-action@v1
with:
channel-id: 'C01234567'
slack-message: |
:white_check_mark: *ABAP Pipeline Successful*
Repository: ${{ github.repository }}
Deployed to: ${{ matrix.environment }}
env:
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

Troubleshooting

Häufige Probleme und Lösungen

ProblemUrsacheLösung
SSL-Zertifikat-FehlerZertifikat fehlt in STRUSTZertifikat importieren
401 UnauthorizedToken-BerechtigungenToken mit repo Scope erstellen
Merge-KonflikteGleichzeitige ÄnderungenIm Git-Repository auflösen, dann Pull
Objekt-SperrenBenutzer hat SperreSM12: Sperre freigeben
ATC TimeoutZu viele ObjektePaket aufteilen oder Timeout erhöhen

Logs abrufen

METHOD get_repository_logs.
DATA(lo_client) = create_gcts_client( ).
DATA(lo_request) = lo_client->get_http_request( ).
lo_request->set_uri_path(
|/sap/bc/cts_abapvcs/repository/{ iv_repository }/log| ).
DATA(lo_response) = lo_client->execute( if_web_http_client=>get ).
IF lo_response->get_status( )-code = 200.
rv_logs = lo_response->get_text( ).
ENDIF.
lo_client->close( ).
ENDMETHOD.

Best Practices Zusammenfassung

ThemaEmpfehlung
Test-IsolationCDS Test Environment für Unit Tests
Test-DatenMock-Daten in Setup-Methoden
ATC-VarianteEigene CI/CD-Variante mit sinnvollen Checks
SecretsGitHub Secrets / Azure Key Vault nutzen
ParallelisierungUnabhängige Jobs parallel ausführen
EnvironmentsGitHub Environments für Approval-Workflows
Commit-MessagesKonventionelle Commits (feat:, fix:, etc.)
Code ReviewPull Requests vor jedem Merge
TaggingReleases mit semantischer Versionierung
MonitoringSlack/Teams-Integration für Benachrichtigungen

Fazit

gCTS und CI/CD transformieren die ABAP-Entwicklung von manuellen Transportprozessen zu automatisierten DevOps-Workflows:

  1. gCTS ermöglicht Git-basiertes Transport-Management mit vollständiger Historie und Branching
  2. CI/CD-Pipelines automatisieren Tests, Qualitätsprüfungen und Deployments
  3. ABAP Unit Tests in der Pipeline sichern die Code-Qualität bei jedem Commit
  4. ATC Checks verhindern, dass Sicherheitslücken oder Performance-Probleme in Produktion gelangen

Nächste Schritte:

  1. ✅ gCTS-Repository für dein ABAP-Projekt einrichten
  2. ✅ GitHub Actions oder Azure DevOps Pipeline konfigurieren
  3. ✅ ABAP Unit Tests für kritische Businesslogik schreiben
  4. ✅ Custom ATC-Variante für CI/CD erstellen
  5. ✅ Team in Git-Workflows schulen

Weiterführende Artikel