diff --git a/.github/actions/deploy-central-snapshot/action.yml b/.github/actions/deploy-central-snapshot/action.yml
new file mode 100644
index 000000000..0d9bbd755
--- /dev/null
+++ b/.github/actions/deploy-central-snapshot/action.yml
@@ -0,0 +1,86 @@
+name: Deploy Snapshot to Central Portal
+description: "Deploys a Maven SNAPSHOT package to Sonatype Central Portal Snapshots repository."
+
+inputs:
+ user:
+ description: "Sonatype Central Portal username (same as Maven Central)"
+ required: true
+ password:
+ description: "Sonatype Central Portal password (same as Maven Central)"
+ required: true
+ pgp-pub-key:
+ description: "The public pgp key ID (optional for snapshots but recommended)"
+ required: false
+ pgp-private-key:
+ description: "The private pgp key (optional for snapshots but recommended)"
+ required: false
+ pgp-passphrase:
+ description: "The passphrase for pgp (optional for snapshots but recommended)"
+ required: false
+
+runs:
+ using: composite
+ steps:
+ - name: "Setup Java"
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'sapmachine'
+ java-version: '21'
+ cache: maven
+ server-id: central
+ server-username: CENTRAL_USER
+ server-password: CENTRAL_PASSWORD
+
+ - name: "Import GPG Key (if provided)"
+ if: ${{ inputs.pgp-private-key != '' }}
+ run: |
+ set +x
+ echo "::add-mask::$PGP_PRIVATE_KEY"
+ echo "::add-mask::$PASSPHRASE"
+ echo "$PGP_PRIVATE_KEY" | gpg --batch --passphrase "$PASSPHRASE" --import
+ shell: bash
+ env:
+ PGP_PRIVATE_KEY: ${{ inputs.pgp-private-key }}
+ PASSPHRASE: ${{ inputs.pgp-passphrase }}
+
+ - name: "Verify SNAPSHOT version"
+ run: |
+ VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
+ echo "Current version: $VERSION"
+ if [[ ! "$VERSION" == *-SNAPSHOT ]]; then
+ echo "Error: Version $VERSION is not a SNAPSHOT version!"
+ echo "Central Portal Snapshots repository only accepts SNAPSHOT versions."
+ exit 1
+ fi
+ echo "✅ Version $VERSION is a valid SNAPSHOT"
+ shell: bash
+
+ - name: "Deploy Snapshot to Central Portal"
+ run: |
+ set +x
+ echo "::add-mask::$CENTRAL_USER"
+ echo "::add-mask::$CENTRAL_PASSWORD"
+ [ -n "$GPG_PASSPHRASE" ] && echo "::add-mask::$GPG_PASSPHRASE"
+ [ -n "$GPG_PUB_KEY" ] && echo "::add-mask::$GPG_PUB_KEY"
+ echo "🚀 Deploying SNAPSHOT to Sonatype Central Portal..."
+ if [ -n "$GPG_PASSPHRASE" ] && [ -n "$GPG_PUB_KEY" ]; then
+ mvn -B -ntp --show-version \
+ -Dmaven.install.skip=true \
+ -Dmaven.test.skip=true \
+ -Dgpg.passphrase="$GPG_PASSPHRASE" \
+ -Dgpg.keyname="$GPG_PUB_KEY" \
+ clean deploy -P deploy-central-snapshot
+ else
+ mvn -B -ntp --show-version \
+ -Dmaven.install.skip=true \
+ -Dmaven.test.skip=true \
+ -Dgpg.skip=true \
+ clean deploy -P deploy-central-snapshot
+ fi
+ echo "✅ Snapshot deployed successfully!"
+ shell: bash
+ env:
+ CENTRAL_USER: ${{ inputs.user }}
+ CENTRAL_PASSWORD: ${{ inputs.password }}
+ GPG_PASSPHRASE: ${{ inputs.pgp-passphrase }}
+ GPG_PUB_KEY: ${{ inputs.pgp-pub-key }}
diff --git a/.github/workflows/deploy-central-snapshot.yml b/.github/workflows/deploy-central-snapshot.yml
new file mode 100644
index 000000000..606a33d7c
--- /dev/null
+++ b/.github/workflows/deploy-central-snapshot.yml
@@ -0,0 +1,125 @@
+name: Deploy Snapshot to Central Portal
+
+env:
+ JAVA_VERSION: '21'
+
+on:
+ # Manual trigger - select any branch from GitHub UI
+ workflow_dispatch:
+ inputs:
+ sign_artifacts:
+ description: 'Sign artifacts with GPG'
+ required: false
+ default: 'true'
+ type: choice
+ options:
+ - 'true'
+ - 'false'
+
+ # Auto-trigger on push to testing branch
+ push:
+ branches:
+ - RBSDMS-NoCqnSnapshot-feature
+
+
+permissions:
+ contents: read
+ packages: read
+
+jobs:
+ verify-snapshot:
+ runs-on: ubuntu-latest
+ outputs:
+ is_snapshot: ${{ steps.check.outputs.is_snapshot }}
+ version: ${{ steps.check.outputs.version }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+
+ - name: Set up Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: ${{ env.JAVA_VERSION }}
+ cache: maven
+
+ - name: Check version is SNAPSHOT
+ id: check
+ run: |
+ VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ if [[ "$VERSION" == *-SNAPSHOT ]]; then
+ echo "is_snapshot=true" >> $GITHUB_OUTPUT
+ echo "✅ Version $VERSION is a SNAPSHOT"
+ else
+ echo "is_snapshot=false" >> $GITHUB_OUTPUT
+ echo "❌ Version $VERSION is NOT a SNAPSHOT - deployment will be skipped"
+ fi
+
+ build:
+ runs-on: ubuntu-latest
+ needs: verify-snapshot
+ if: needs.verify-snapshot.outputs.is_snapshot == 'true'
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+
+ - name: Set up Java
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: ${{ env.JAVA_VERSION }}
+ cache: maven
+
+ - name: Build
+ run: |
+ echo "🔨 Building SNAPSHOT version: ${{ needs.verify-snapshot.outputs.version }}"
+ mvn clean install -P unit-tests -DskipIntegrationTests
+ echo "✅ Build completed successfully!"
+
+ deploy:
+ name: Deploy Snapshot to Central Portal
+ runs-on: ubuntu-latest
+ needs: [verify-snapshot, build]
+ if: needs.verify-snapshot.outputs.is_snapshot == 'true' && needs.build.result == 'success'
+ environment: maven-central
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6
+
+ - name: Deploy Snapshot (with GPG signing)
+ if: github.event.inputs.sign_artifacts != 'false'
+ uses: ./.github/actions/deploy-central-snapshot
+ with:
+ user: ${{ secrets.CENTRAL_REPOSITORY_USER }}
+ password: ${{ secrets.CENTRAL_REPOSITORY_PASS }}
+ pgp-pub-key: ${{ secrets.PGP_PUB_KEY }}
+ pgp-private-key: ${{ secrets.PGP_PRIVATE_KEY }}
+ pgp-passphrase: ${{ secrets.PGP_PASSPHRASE }}
+
+ - name: Deploy Snapshot (without GPG signing)
+ if: github.event.inputs.sign_artifacts == 'false'
+ uses: ./.github/actions/deploy-central-snapshot
+ with:
+ user: ${{ secrets.CENTRAL_REPOSITORY_USER }}
+ password: ${{ secrets.CENTRAL_REPOSITORY_PASS }}
+
+ - name: Summary
+ if: success()
+ run: |
+ echo "## 🚀 Snapshot Deployed to Central Portal" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "**Version:** ${{ needs.verify-snapshot.outputs.version }}" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "**Repository:** https://central.sonatype.com/repository/maven-snapshots/" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "### Usage" >> $GITHUB_STEP_SUMMARY
+ echo '```xml' >> $GITHUB_STEP_SUMMARY
+ echo '' >> $GITHUB_STEP_SUMMARY
+ echo ' ' >> $GITHUB_STEP_SUMMARY
+ echo ' central-snapshots' >> $GITHUB_STEP_SUMMARY
+ echo ' https://central.sonatype.com/repository/maven-snapshots/' >> $GITHUB_STEP_SUMMARY
+ echo ' true' >> $GITHUB_STEP_SUMMARY
+ echo ' ' >> $GITHUB_STEP_SUMMARY
+ echo '' >> $GITHUB_STEP_SUMMARY
+ echo '```' >> $GITHUB_STEP_SUMMARY
diff --git a/pom.xml b/pom.xml
index 250ac7fe1..c319e0129 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
- 1.9.2
+ 1.9.3-SNAPSHOT
17
${java.version}
${java.version}
@@ -346,6 +346,34 @@
+
+ deploy-central-snapshot
+
+
+
+ central
+ Sonatype Central Portal Snapshots
+ https://central.sonatype.com/repository/maven-snapshots/
+
+
+
+ disabled-release
+ file:///dev/null
+
+
+
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ false
+
+ true
+
+
+
+
+
diff --git a/sdm/pom.xml b/sdm/pom.xml
index 8210bbc7d..58ceff0fd 100644
--- a/sdm/pom.xml
+++ b/sdm/pom.xml
@@ -97,6 +97,20 @@
+
+ deploy-central-snapshot
+
+
+ central
+ Sonatype Central Portal Snapshots
+ https://central.sonatype.com/repository/maven-snapshots/
+
+
+ disabled-release
+ file:///dev/null
+
+
+
diff --git a/sdm/src/main/java/com/sap/cds/sdm/handler/applicationservice/SDMCreateAttachmentsHandler.java b/sdm/src/main/java/com/sap/cds/sdm/handler/applicationservice/SDMCreateAttachmentsHandler.java
index 01021a263..ca12ecdbf 100644
--- a/sdm/src/main/java/com/sap/cds/sdm/handler/applicationservice/SDMCreateAttachmentsHandler.java
+++ b/sdm/src/main/java/com/sap/cds/sdm/handler/applicationservice/SDMCreateAttachmentsHandler.java
@@ -109,7 +109,7 @@ public void processBefore(CdsCreateEventContext context, List data) thr
logger.info(
"START: Process attachments before persistence for entity: {}",
context.getTarget().getQualifiedName());
- logger.debug("Number of entities to process: {}", data.size());
+ logger.info("Number of entities to process: {}", data.size());
for (CdsData entityData : data) {
Map> attachmentCompositionDetails =
@@ -119,7 +119,7 @@ public void processBefore(CdsCreateEventContext context, List data) thr
persistenceService,
context.getTarget().getQualifiedName(),
entityData);
- logger.debug("Attachment compositions present: {}", attachmentCompositionDetails.keySet());
+ logger.info("Attachment compositions found: {}", attachmentCompositionDetails.keySet());
updateName(context, data, attachmentCompositionDetails);
// Remove uploadStatus from attachment data to prevent validation errors
cleanupReadonlyContextsForAttachments(context, entityData, attachmentCompositionDetails);
@@ -151,32 +151,45 @@ public void processAfter(CdsCreateEventContext context, List data) {
Optional attachmentEntity =
context.getModel().findEntity(attachmentCompositionDefinition);
- if (attachmentEntity.isPresent()) {
- String targetEntity = context.getTarget().getQualifiedName();
- List