diff --git a/.github/workflows/action.yaml b/.github/workflows/action.yaml
new file mode 100644
index 00000000..6e67fde7
--- /dev/null
+++ b/.github/workflows/action.yaml
@@ -0,0 +1,204 @@
+name: Backend CI/CD
+
+env:
+ DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
+ DOCKER_PAT: ${{ secrets.DOCKER_PAT }}
+ IMAGE_NAME: eschool-backend
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ sonar:
+ name: SonarQube Scan
+ runs-on: ubuntu-latest
+ if: github.event_name == 'pull_request'
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+ cache: 'maven'
+
+ - name: Build and Sonar Analysis
+ env:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
+ run: |
+ mvn clean verify sonar:sonar \
+ -Dsonar.projectKey=eschool-backend-final-project \
+ -Dsonar.host.url=$SONAR_HOST_URL \
+ -Dsonar.token=$SONAR_TOKEN \
+ -Dsonar.pullrequest.key=${{ github.event.pull_request.number }} \
+ -Dsonar.pullrequest.branch=${{ github.head_ref }} \
+ -Dsonar.pullrequest.base=master \
+ -DskipTests
+
+ - name: SonarQube Quality Gate check
+ id: sonarqube-quality-gate-check
+ uses: sonarsource/sonarqube-quality-gate-action@master
+ with:
+ scanMetadataReportFile: target/sonar/report-task.txt
+ pollingTimeoutSec: 600
+ env:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
+
+ - name: "Example show SonarQube Quality Gate Status value"
+ run: echo "The Quality Gate status is ${{ steps.sonarqube-quality-gate-check.outputs.quality-gate-status }}"
+
+ - name: Get SonarQube Report (New Code + Overall)
+ id: sonar-report
+ if: github.event_name == 'pull_request'
+ env:
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
+ run: |
+ NEW_CODE=$(curl -s -u "$SONAR_TOKEN:" \
+ "$SONAR_HOST_URL/api/measures/component?component=eschool-backend-final-project&metricKeys=software_quality_security_rating,software_quality_reliability_rating,duplicated_lines_density,software_quality_reliability_issues,vulnerabilities,code_smells,coverage,security_hotspots&additionalFields=period")
+
+ OVERALL=$(curl -s -u "$SONAR_TOKEN:" \
+ "$SONAR_HOST_URL/api/measures/component?component=eschool-backend-final-project&metricKeys=software_quality_security_rating,software_quality_reliability_rating,duplicated_lines_density,software_quality_reliability_issues,vulnerabilities,code_smells,coverage,security_hotspots,lines,ncloc")
+
+ echo "new_code_b64=$(echo "$NEW_CODE" | base64 -w 0)" >> $GITHUB_OUTPUT
+ echo "overall_b64=$(echo "$OVERALL" | base64 -w 0)" >> $GITHUB_OUTPUT
+
+ - name: Post SonarQube results to PR
+ if: github.event_name == 'pull_request'
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const qualityGateStatus = '${{ steps.sonarqube-quality-gate-check.outputs.quality-gate-status }}';
+
+ try {
+ const newCodeJson = Buffer
+ .from('${{ steps.sonar-report.outputs.new_code_b64 }}', 'base64')
+ .toString();
+
+ const overallJson = Buffer
+ .from('${{ steps.sonar-report.outputs.overall_b64 }}', 'base64')
+ .toString();
+
+ const newCode = JSON.parse(newCodeJson);
+ const overall = JSON.parse(overallJson);
+
+ function extractMeasures(data) {
+ const result = {};
+ data.component.measures.forEach(m => {
+ result[m.metric] = {
+ overall: m.value ?? '0',
+ new: m.period?.value ?? '0'
+ };
+ });
+ return result;
+ }
+
+ const newMetrics = extractMeasures(newCode);
+ const overallMetrics = extractMeasures(overall);
+
+ const body = `
+ ## SonarQube Analysis
+
+ ### Quality Gate: **${qualityGateStatus}**
+
+ ### New Code
+ - Security rating on new code: ${newMetrics.software_quality_security_rating?.new}
+ - Reliability rating on new code: ${newMetrics.software_quality_reliability_rating?.new}
+ - Duplicated lines density (%) on new code: ${newMetrics.duplicated_lines_density?.new}
+ - Bugs: ${newMetrics.software_quality_reliability_issues?.new}
+ - Vulnerabilities: ${newMetrics.vulnerabilities?.new}
+ - Code Smells: ${newMetrics.code_smells?.new}
+ - Coverage: ${newMetrics.coverage?.new}%
+ - Security hotspots on new code ${newMetrics.security_hotspots?.new}
+
+ ---
+
+ ### Overall Code
+ - Security Rating: ${overallMetrics.software_quality_security_rating?.overall}
+ - Reliability rating: ${overallMetrics.software_quality_reliability_rating?.overall}
+ - Duplicated lines density (%): ${overallMetrics.duplicated_lines_density?.overall}
+ - Bugs: ${overallMetrics.software_quality_reliability_issues?.overall}
+ - Vulnerabilities: ${overallMetrics.vulnerabilities?.overall}
+ - Code Smells: ${overallMetrics.code_smells?.overall}
+ - Coverage: ${overallMetrics.coverage?.overall}%
+ - Security hotspots ${overallMetrics.security_hotspots?.overall}
+ - Lines: ${overallMetrics.lines?.overall}
+ - ncloc: ${overallMetrics.ncloc?.overall}
+
+ [View Full Report](${{ secrets.SONAR_HOST_URL }}/dashboard)
+ `;
+
+ await github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body
+ });
+
+ } catch (error) {
+ console.error(error);
+ }
+
+ # docker-build-and-push:
+ # name: Build & Push Docker image
+ # runs-on: ubuntu-latest
+ # if: github.event_name != 'pull_request'
+ # steps:
+ # - uses: actions/checkout@v4
+
+ # - name: Docker login
+ # run: echo "$DOCKER_PAT" | docker login -u "$DOCKER_USERNAME" --password-stdin
+
+ # - name: Build image
+ # run: |
+ # docker build \
+ # -t $DOCKER_USERNAME/$IMAGE_NAME:backend-${GITHUB_RUN_NUMBER} \
+ # -t $DOCKER_USERNAME/$IMAGE_NAME:backend-latest \
+ # .
+
+ # - name: Push image
+ # run: |
+ # docker push $DOCKER_USERNAME/$IMAGE_NAME:backend-${GITHUB_RUN_NUMBER}
+ # docker push $DOCKER_USERNAME/$IMAGE_NAME:backend-latest
+
+ # deploy:
+ # name: Deploy Backend
+ # runs-on: ubuntu-latest
+ # needs: docker-build-and-push
+ # if: github.event_name != 'pull_request'
+ # steps:
+ # - name: Checkout
+ # uses: actions/checkout@v4
+
+ # - name: SSH and deploy
+ # env:
+ # PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
+ # SERVER_IP: ${{ secrets.SERVER_IP }}
+ # DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
+ # DOCKER_PAT: ${{ secrets.DOCKER_PAT }}
+ # IMAGE_NAME: eschool
+ # run: |
+ # echo "$PRIVATE_KEY" > private_key.pem
+ # chmod 600 private_key.pem
+ # ssh -o StrictHostKeyChecking=no -i private_key.pem ec2-user@$SERVER_IP << 'EOF'
+ # export DOCKER_USERNAME=${{ secrets.DOCKER_USERNAME }}
+ # export DOCKER_PAT=${{ secrets.DOCKER_PAT }}
+ # export IMAGE_NAME=eschool
+
+ # echo "$DOCKER_PAT" | sudo docker login -u "$DOCKER_USERNAME" --password-stdin
+ # sudo docker pull $DOCKER_USERNAME/$IMAGE_NAME:backend-latest
+
+ # sudo docker compose up -d --force-recreate backend
+
+ # EOF
+ # rm -f private_key.pem
diff --git a/Dockerfile b/Dockerfile
index d0af674f..93b29191 100755
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,10 @@
-FROM maven:3.5.2-jdk-8-alpine AS MAVEN_TOOL_CHAIN
+FROM maven:3.5.2-jdk-8 AS MAVEN_TOOL_CHAIN
COPY pom.xml /tmp/
COPY src /tmp/src/
WORKDIR /tmp/
-RUN mvn package
+RUN mvn package -DskipTests
-FROM openjdk:8-jdk-alpine
+FROM tomcat:9.0-jre8-alpine
COPY --from=MAVEN_TOOL_CHAIN /tmp/target/eschool.jar eschool.jar
-EXPOSE 8080
+EXPOSE 8081
ENTRYPOINT ["java", "-jar", "eschool.jar"]
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index f86c7b04..09f75815 100755
--- a/pom.xml
+++ b/pom.xml
@@ -103,7 +103,7 @@
org.projectlombok
lombok
- 1.18.0
+ 1.18.30
provided
@@ -166,7 +166,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 3.6.1
+ 3.11.0
1.8
1.8