204 lines
6.6 KiB
YAML
204 lines
6.6 KiB
YAML
name: Notification Service – CI/CD
|
||
|
||
on:
|
||
workflow_dispatch:
|
||
push:
|
||
paths:
|
||
- notification_service/**
|
||
- .github/workflows/notification-service.yml
|
||
pull_request:
|
||
branches: [main]
|
||
paths:
|
||
- notification_service/**
|
||
- .github/workflows/notification-service.yml
|
||
|
||
permissions:
|
||
contents: read
|
||
security-events: write
|
||
|
||
jobs:
|
||
# ── 1. Build & test ──────────────────────────────────────────────────────────
|
||
ci:
|
||
name: Build & test
|
||
runs-on: ubuntu-latest
|
||
defaults:
|
||
run:
|
||
working-directory: notification_service
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Setup Java 17
|
||
uses: actions/setup-java@v4
|
||
with:
|
||
java-version: 17
|
||
distribution: temurin
|
||
cache: maven
|
||
|
||
- name: Build & test (SpotBugs included via verify)
|
||
run: mvn verify -q
|
||
|
||
# ── 2. Dependency vulnerability scan (OWASP) ─────────────────────────────────
|
||
dependency-check:
|
||
name: Dependency vulnerability scan
|
||
runs-on: ubuntu-latest
|
||
defaults:
|
||
run:
|
||
working-directory: notification_service
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Setup Java 17
|
||
uses: actions/setup-java@v4
|
||
with:
|
||
java-version: 17
|
||
distribution: temurin
|
||
cache: maven
|
||
|
||
- name: Cache NVD database
|
||
uses: actions/cache@v4
|
||
with:
|
||
path: ~/.m2/repository/org/owasp/dependency-check-data
|
||
key: nvd-${{ runner.os }}-${{ hashFiles('notification_service/pom.xml') }}
|
||
restore-keys: nvd-${{ runner.os }}-
|
||
|
||
- name: OWASP Dependency Check
|
||
run: mvn dependency-check:check -q
|
||
continue-on-error: true
|
||
|
||
- name: Upload dependency check report
|
||
if: always()
|
||
uses: actions/upload-artifact@v4
|
||
with:
|
||
name: dependency-check-report
|
||
path: notification_service/target/dependency-check-report.*
|
||
retention-days: 14
|
||
|
||
# ── 4. DAST – OWASP ZAP ──────────────────────────────────────────────────────
|
||
dast:
|
||
name: DAST – OWASP ZAP
|
||
runs-on: ubuntu-latest
|
||
needs: ci
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Setup Java 17
|
||
uses: actions/setup-java@v4
|
||
with:
|
||
java-version: 17
|
||
distribution: temurin
|
||
cache: maven
|
||
|
||
- name: Build JAR
|
||
run: mvn package -DskipTests -q -f notification_service/pom.xml
|
||
|
||
- name: Start service
|
||
run: |
|
||
java -jar notification_service/target/notification-service-*.jar &
|
||
timeout 60 bash -c 'until curl -sf http://localhost:8080/actuator/health; do sleep 2; done'
|
||
env:
|
||
TELEGRAM_BOT_TOKEN: dummy-token
|
||
TELEGRAM_CHAT_ID: "0"
|
||
AIR_QUALITY_API_URL: http://localhost:9999
|
||
API_USERNAME: dummy
|
||
API_PASSWORD: dummy
|
||
MOCK_MODE: "true"
|
||
|
||
- name: ZAP Baseline Scan
|
||
uses: zaproxy/action-baseline@v0.14.0
|
||
with:
|
||
target: http://localhost:8080/actuator/health
|
||
fail_action: false
|
||
allow_issue_writing: false
|
||
artifact_name: zap-report
|
||
|
||
# ── 5. Docker build & push ───────────────────────────────────────────────────
|
||
docker:
|
||
name: Docker build & push
|
||
runs-on: ubuntu-latest
|
||
needs: [ci, dast]
|
||
if: github.ref == 'refs/heads/main'
|
||
outputs:
|
||
sha_tag: ${{ steps.tag.outputs.sha }}
|
||
|
||
permissions:
|
||
contents: read
|
||
packages: write
|
||
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Compute image tags
|
||
id: tag
|
||
run: |
|
||
OWNER="${{ github.repository_owner }}"
|
||
IMAGE_LC="ghcr.io/${OWNER,,}/notification-service"
|
||
echo "image=${IMAGE_LC}" >> $GITHUB_OUTPUT
|
||
echo "sha=${IMAGE_LC}:${{ github.sha }}" >> $GITHUB_OUTPUT
|
||
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
|
||
echo "extra=${IMAGE_LC}:latest" >> $GITHUB_OUTPUT
|
||
else
|
||
BRANCH="${{ github.head_ref || github.ref_name }}"
|
||
echo "extra=${IMAGE_LC}:branch-${BRANCH//\//-}" >> $GITHUB_OUTPUT
|
||
fi
|
||
|
||
- name: Login to GHCR
|
||
uses: docker/login-action@v3
|
||
with:
|
||
registry: ghcr.io
|
||
username: ${{ github.actor }}
|
||
password: ${{ secrets.GITHUB_TOKEN }}
|
||
|
||
- name: Build & push
|
||
uses: docker/build-push-action@v5
|
||
with:
|
||
context: notification_service
|
||
push: true
|
||
tags: |
|
||
${{ steps.tag.outputs.sha }}
|
||
${{ steps.tag.outputs.extra }}
|
||
|
||
# ── 6. Deploy to physical server (SSH) ───────────────────────────────────────
|
||
deploy:
|
||
name: Deploy
|
||
runs-on: ubuntu-latest
|
||
needs: docker
|
||
if: github.ref == 'refs/heads/main'
|
||
|
||
steps:
|
||
- name: SSH deploy
|
||
env:
|
||
SSH_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||
SSH_CERT: ${{ secrets.SSH_CERTIFICATE }}
|
||
SHA_TAG: ${{ needs.docker.outputs.sha_tag }}
|
||
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
|
||
AIR_QUALITY_API_URL: ${{ secrets.AIR_QUALITY_API_URL }}
|
||
API_USERNAME: ${{ secrets.API_USERNAME }}
|
||
API_PASSWORD: ${{ secrets.API_PASSWORD }}
|
||
run: |
|
||
echo "$SSH_KEY" > /tmp/deploy_key
|
||
echo "$SSH_CERT" > /tmp/deploy_key-cert.pub
|
||
chmod 600 /tmp/deploy_key
|
||
ssh -i /tmp/deploy_key \
|
||
-p ${{ secrets.SSH_PORT }} \
|
||
-o StrictHostKeyChecking=no \
|
||
-o IdentitiesOnly=yes \
|
||
${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \
|
||
"sudo -iu e << 'EOF'
|
||
docker pull $SHA_TAG
|
||
docker stop notification-service 2>/dev/null || true
|
||
docker rm notification-service 2>/dev/null || true
|
||
docker run -d --name notification-service --restart unless-stopped \
|
||
-e TELEGRAM_BOT_TOKEN=$TELEGRAM_BOT_TOKEN \
|
||
-e TELEGRAM_CHAT_ID=$TELEGRAM_CHAT_ID \
|
||
-e AIR_QUALITY_API_URL=$AIR_QUALITY_API_URL \
|
||
-e API_USERNAME=$API_USERNAME \
|
||
-e API_PASSWORD=$API_PASSWORD \
|
||
-e MOCK_MODE=false \
|
||
$SHA_TAG
|
||
EOF"
|
||
rm /tmp/deploy_key /tmp/deploy_key-cert.pub
|