Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
373 changes: 373 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,373 @@
name: Continuous Integration

on:
push:
branches: [ main ]
tags: [ 'v*' ]
pull_request:
branches: [ main ]

env:
JAVA_VERSION: '21'
MAVEN_VERSION: '3.9.9'
MAVEN_OPTS: '-Xmx2048m -XX:+TieredCompilation -XX:TieredStopAtLevel=1'

jobs:
# ========================================================================
# BUILD AND TEST
# ========================================================================
build:
name: Build and Test
runs-on: ubuntu-latest

strategy:
matrix:
java: [ '21' ]
maven: [ '3.9.9' ]

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: ${{ matrix.java }}
cache: 'maven'

- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: |
~/.m2/repository
~/.m2/wrapper
target/
key: ${{ runner.os }}-maven-${{ matrix.java }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-${{ matrix.java }}-
${{ runner.os }}-maven-

- name: Cache Maven wrapper
uses: actions/cache@v4
with:
path: ~/.m2/wrapper
key: ${{ runner.os }}-maven-wrapper-${{ hashFiles('**/maven-wrapper.properties') }}
restore-keys: ${{ runner.os }}-maven-wrapper

- name: Validate POM
run: mvn validate

- name: Compile
run: mvn compile -B

- name: Run unit tests
run: mvn test -B

- name: Run integration tests
run: mvn verify -B

- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results-${{ matrix.java }}-${{ matrix.maven }}
path: |
target/surefire-reports/
target/failsafe-reports/
target/jacoco/
retention-days: 30

- name: Build package
run: mvn package -B -DskipTests

- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts-${{ matrix.java }}-${{ matrix.maven }}
path: |
target/*.jar
target/*.war
target/*.ear
retention-days: 30

# ========================================================================
# QUALITY CHECKS
# ========================================================================
quality:
name: Quality Analysis
runs-on: ubuntu-latest
needs: build

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'

- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: |
~/.m2/repository
~/.m2/wrapper
target/
key: ${{ runner.os }}-maven-${{ env.JAVA_VERSION }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-${{ env.JAVA_VERSION }}-
${{ runner.os }}-maven-

- name: Run PMD analysis
run: mvn pmd:check -B

- name: Run SpotBugs analysis
run: mvn spotbugs:check -B

- name: Run Checkstyle
run: mvn checkstyle:check -B

- name: Generate JaCoCo report
run: mvn jacoco:report -B

- name: Upload quality reports
uses: actions/upload-artifact@v4
if: always()
with:
name: quality-reports
path: |
target/pmd/
target/spotbugs/
target/site/checkstyle.html
target/site/jacoco/
retention-days: 30

# ========================================================================
# SECURITY SCAN
# ========================================================================
security:
name: Security Scan
runs-on: ubuntu-latest
needs: build

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'

- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: |
~/.m2/repository
~/.m2/wrapper
target/
key: ${{ runner.os }}-maven-${{ env.JAVA_VERSION }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-${{ env.JAVA_VERSION }}-
${{ runner.os }}-maven-

- name: Cache OWASP Dependency Check
uses: actions/cache@v4
with:
path: ~/.cache/owasp
key: ${{ runner.os }}-owasp-${{ hashFiles('**/pom.xml') }}
restore-keys: ${{ runner.os }}-owasp-

- name: Run OWASP Dependency Check
run: mvn org.owasp:dependency-check-maven:8.4.3:check -B
env:
JAVA_HOME: ${{ env.JAVA_HOME }}
JAVA_VERSION: ${{ env.JAVA_VERSION }}

- name: Upload security reports
uses: actions/upload-artifact@v4
if: always()
with:
name: security-reports
path: target/dependency-check/
retention-days: 30

# ========================================================================
# SITE GENERATION
# ========================================================================
site:
name: Generate Site
runs-on: ubuntu-latest
needs: [build, quality]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'

- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: |
~/.m2/repository
~/.m2/wrapper
target/
key: ${{ runner.os }}-maven-${{ env.JAVA_VERSION }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-${{ env.JAVA_VERSION }}-
${{ runner.os }}-maven-

- name: Generate Maven site
run: mvn site:site -B

- name: Upload site artifacts
uses: actions/upload-artifact@v4
with:
name: maven-site
path: target/site/
retention-days: 30

# ========================================================================
# PUBLISH ARTIFACTS
# ========================================================================
publish:
name: Publish Artifacts
runs-on: ubuntu-latest
needs: [build, quality, security]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: ${{ env.JAVA_VERSION }}
cache: 'maven'

- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: |
~/.m2/repository
~/.m2/wrapper
target/
key: ${{ runner.os }}-maven-${{ env.JAVA_VERSION }}-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-${{ env.JAVA_VERSION }}-
${{ runner.os }}-maven-

- name: Setup GPG
uses: crazy-max/ghaction-import-gpg@v6
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}

- name: Publish to GitHub Packages
run: mvn deploy -B -P release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Create Release
uses: actions/create-release@v1
if: startsWith(github.ref, 'refs/tags/')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false

# ========================================================================
# DEPLOY SITE
# ========================================================================
deploy-site:
name: Deploy Site
runs-on: ubuntu-latest
needs: [site, publish]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Download site artifacts
uses: actions/download-artifact@v4
with:
name: maven-site
path: target/site

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./target/site
force_orphan: true
user_name: 'github-actions[bot]'
user_email: 'github-actions[bot]@users.noreply.github.com'

# ========================================================================
# NOTIFICATIONS
# ========================================================================
notify:
name: Notifications
runs-on: ubuntu-latest
needs: [build, quality, security, publish, deploy-site]
if: always()

steps:
- name: Check Slack webhook
id: check-slack
run: |
if [ -n "${{ secrets.SLACK_WEBHOOK_URL }}" ]; then
echo "slack_enabled=true" >> $GITHUB_OUTPUT
else
echo "slack_enabled=false" >> $GITHUB_OUTPUT
fi

- name: Notify on failure
if: failure() && steps.check-slack.outputs.slack_enabled == 'true'
uses: 8398a7/action-slack@v3
with:
status: failure
webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
channel: '#ci-notifications'
text: '❌ CI failed for ${{ github.repository }}#${{ github.run_number }}'

- name: Notify on success
if: success() && steps.check-slack.outputs.slack_enabled == 'true'
uses: 8398a7/action-slack@v3
with:
status: success
webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }}
channel: '#ci-notifications'
text: '✅ CI passed for ${{ github.repository }}#${{ github.run_number }}'
Loading
Loading