diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..6e166e90
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,259 @@
+# This workflow handles the complete release process for static-code-analysis
+# It converts snapshot to release version, deploys to Maven Central via OSSRH,
+# and then bumps to the next snapshot version
+
+name: Release to Maven Central
+permissions:
+ contents: write
+
+on:
+ workflow_dispatch:
+ inputs:
+ release_version:
+ description: 'Release version (e.g., 0.18.0)'
+ required: true
+ type: string
+ next_snapshot_version:
+ description: 'Next snapshot version (e.g., 0.19.0-SNAPSHOT)'
+ required: true
+ type: string
+
+jobs:
+ release:
+ name: Release to Maven Central
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ fetch-depth: 0
+
+ - name: Set up JDK 11
+ uses: actions/setup-java@v5
+ with:
+ java-version: 11
+ distribution: 'temurin'
+ cache: maven
+ server-id: ossrh
+ server-username: OSSRH_USERNAME
+ server-password: OSSRH_PASSWORD
+ gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+ gpg-passphrase: GPG_PASSPHRASE
+
+ - name: Configure Git
+ run: |
+ git config --global user.name "github-actions[bot]"
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+
+ - name: Update version to release version
+ run: |
+ mvn versions:set -DnewVersion=${{ github.event.inputs.release_version }}
+ mvn versions:commit
+
+ - name: Update parent POM versions in modules
+ run: |
+ # Update parent version in all child POMs
+ for pom in $(find . -name pom.xml -not -path "*/target/*" -not -path "*/src/test/resources/*"); do
+ if [ -f "$pom" ]; then
+ mvn versions:update-parent -DparentVersion=${{ github.event.inputs.release_version }} -f "$pom"
+ mvn versions:commit -f "$pom"
+ fi
+ done
+
+ - name: Verify release version
+ run: |
+ echo "Updated version to:"
+ mvn help:evaluate -Dexpression=project.version -q -DforceStdout
+
+ - name: Build without tests
+ run: mvn clean compile -DskipTests=true -DskipChecks=true
+
+ - name: Deploy to Maven Central
+ id: maven-deploy
+ env:
+ OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
+ OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+ run: |
+ # Fail fast: If this step fails, no git operations will occur
+ mvn -DskipTests=true -DskipChecks=true -DperformRelease=true clean deploy
+
+ - name: Trigger Central Publisher Portal Upload
+ env:
+ OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
+ OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
+ CENTRAL_NAMESPACE: ${{ secrets.CENTRAL_NAMESPACE }}
+ run: |
+ echo "Triggering manual upload to Central Publisher Portal..."
+
+ # Create base64 encoded credentials for Basic Auth
+ CREDENTIALS=$(echo -n "$OSSRH_USERNAME:$OSSRH_PASSWORD" | base64)
+
+ # Make POST request to trigger upload
+ HTTP_STATUS=$(curl -s -o response.json -w "%{http_code}" \
+ -X POST \
+ -H "Authorization: Basic $CREDENTIALS" \
+ -H "Content-Type: application/json" \
+ "https://ossrh-staging-api.central.sonatype.com/manual/upload/defaultRepository/$CENTRAL_NAMESPACE")
+
+ echo "HTTP Status: $HTTP_STATUS"
+ echo "Response:"
+ cat response.json
+
+ if [ $HTTP_STATUS -eq 200 ] || [ $HTTP_STATUS -eq 201 ]; then
+ echo "✅ Successfully triggered upload to Central Publisher Portal"
+ else
+ echo "❌ Failed to trigger upload (HTTP $HTTP_STATUS)"
+ echo "Response details:"
+ cat response.json
+ exit 1
+ fi
+
+ - name: Collect all POM files for commit
+ # This step only runs if Maven Central deployment succeeded (fail-fast behavior)
+ if: success()
+ run: |
+ # Add all modified tracked POM files
+ git add -u **/pom.xml
+
+ - name: Commit release version and push to main branch
+ # This step only runs if Maven Central deployment succeeded (fail-fast behavior)
+ if: success()
+ run: |
+ git commit -m "Release version ${{ github.event.inputs.release_version }}"
+ git push origin
+
+ - name: Create and push release tag
+ # This step only runs if Maven Central deployment and commit succeeded (fail-fast behavior)
+ if: success()
+ run: |
+ git tag -a "v${{ github.event.inputs.release_version }}" -m "Release version ${{ github.event.inputs.release_version }}"
+ git push origin "v${{ github.event.inputs.release_version }}"
+
+ - name: Update to next snapshot version
+ # This step only runs after successful tag creation (fail-fast behavior)
+ if: success()
+ run: |
+ mvn versions:set -DnewVersion=${{ github.event.inputs.next_snapshot_version }}
+ mvn versions:commit
+
+ - name: Update parent POM versions in modules to snapshot
+ # This step only runs after successful tag creation (fail-fast behavior)
+ if: success()
+ run: |
+ # Update parent version in all child POMs
+ # Find all pom.xml files except the root pom.xml
+ for pom in $(find . -name pom.xml ! -path "./pom.xml"); do
+ if [ -f "$pom" ]; then
+ mvn versions:update-parent -DparentVersion=${{ github.event.inputs.next_snapshot_version }} -f "$pom"
+ mvn versions:commit -f "$pom"
+ fi
+ done
+
+ - name: Verify snapshot version
+ if: success()
+ run: |
+ echo "Updated version to:"
+ mvn help:evaluate -Dexpression=project.version -q -DforceStdout
+
+ - name: Commit and push snapshot version
+ # This step only runs after successful deployment and tag creation (fail-fast behavior)
+ if: success()
+ run: |
+ # Add all modified POM files
+ git add **/pom.xml
+ git commit -m "Prepare for next development iteration: ${{ github.event.inputs.next_snapshot_version }}"
+ git push origin
+
+ - name: Create GitHub Release
+ # This step only runs after all previous steps succeeded (fail-fast behavior)
+ if: success()
+ uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
+ with:
+ tag_name: "v${{ github.event.inputs.release_version }}"
+ name: "Release ${{ github.event.inputs.release_version }}"
+ body: |
+ Release ${{ github.event.inputs.release_version }}
+
+ This release has been automatically deployed to Maven Central.
+
+ **Artifacts:**
+ - `org.openhab.tools.sat:sat-plugin:${{ github.event.inputs.release_version }}`
+ - `org.openhab.tools.sat:sat-extension:${{ github.event.inputs.release_version }}`
+ - `org.openhab.tools:openhab-codestyle:${{ github.event.inputs.release_version }}`
+ - `org.openhab.tools.sat.custom-checks:checkstyle-rules:${{ github.event.inputs.release_version }}`
+ - `org.openhab.tools.sat.custom-checks:pmd-rules:${{ github.event.inputs.release_version }}`
+ - `org.openhab.tools.sat.custom-checks:findbugs-rules:${{ github.event.inputs.release_version }}`
+
+ **Maven Central - SAT Plugin:**
+ ```xml
+
+ org.openhab.tools.sat
+ sat-plugin
+ ${{ github.event.inputs.release_version }}
+
+ ```
+
+ **Maven Central - SAT Extension:**
+ ```xml
+
+ org.openhab.tools.sat
+ sat-extension
+ ${{ github.event.inputs.release_version }}
+
+ ```
+
+ **Maven Central - Codestyle:**
+ ```xml
+
+ org.openhab.tools
+ openhab-codestyle
+ ${{ github.event.inputs.release_version }}
+
+ ```
+ draft: false
+ prerelease: false
+
+ check-central-deployment:
+ name: Verify Maven Central Deployment
+ needs: release
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Wait for Maven Central synchronization
+ run: |
+ echo "Waiting for artifacts to be synchronized to Maven Central..."
+ sleep 300 # Wait 5 minutes for initial propagation
+
+ - name: Check Maven Central availability
+ run: |
+ RELEASE_VERSION="${{ github.event.inputs.release_version }}"
+ MAX_ATTEMPTS=12
+ ATTEMPT=1
+
+ while [ $ATTEMPT -le $MAX_ATTEMPTS ]; do
+ echo "Attempt $ATTEMPT of $MAX_ATTEMPTS: Checking Maven Central for version $RELEASE_VERSION..."
+
+ HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
+ "https://repo1.maven.org/maven2/org/openhab/tools/sat/sat-plugin/$RELEASE_VERSION/sat-plugin-$RELEASE_VERSION.pom")
+
+ if [ $HTTP_STATUS -eq 200 ]; then
+ echo "✅ Artifact successfully found on Maven Central!"
+ echo "🔗 Maven Central URL: https://repo1.maven.org/maven2/org/openhab/tools/sat/sat-plugin/$RELEASE_VERSION/"
+ echo "📦 Maven Central search: https://search.maven.org/artifact/org.openhab.tools.sat/sat-plugin/$RELEASE_VERSION/maven-plugin"
+ break
+ else
+ echo "❌ Artifact not yet available (HTTP $HTTP_STATUS). Waiting..."
+ if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then
+ echo "⚠️ Maximum attempts reached. Deployment may still be in progress."
+ echo "🕐 It can take up to 2 hours for artifacts to appear on Maven Central."
+ echo "📋 Check manually at: https://search.maven.org/artifact/org.openhab.tools.sat/sat-plugin/$RELEASE_VERSION/maven-plugin"
+ else
+ sleep 300 # Wait 5 minutes between attempts
+ fi
+ fi
+
+ ATTEMPT=$((ATTEMPT + 1))
+ done