diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml index 6c38e84264dd791f075728c170058958abe5761c..ca00582aefad65f9b1f448d03ec2886ca5bf5ef0 100644 --- a/.github/workflows/maven-build.yml +++ b/.github/workflows/maven-build.yml @@ -1,3 +1,4 @@ +--- name: Java CI with Maven on: @@ -17,7 +18,7 @@ jobs: uses: actions/setup-java@v2 with: java-version: '17' - distribution: 'adopt' + distribution: 'temurin' cache: maven - name: Build with Maven run: mvn -B install --file pom.xml -Djacoco.skip=true -DdisableXmlReport=true diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..876083889e4c04999741c27f96d13cd897aab10a --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,139 @@ +--- +# Based on the Maven CI/CD template from GitLab: https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Maven.gitlab-ci.yml +variables: + # This will suppress any download for dependencies and plugins or upload messages which would clutter the console log. + # `showDateTime` will show the passed time in milliseconds. You need to specify `--batch-mode` to make this work. + MAVEN_OPTS: > + -Dhttps.protocols=TLSv1.2 + -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN + -Dorg.slf4j.simpleLogger.showDateTime=true + -Djava.awt.headless=true + # As of Maven 3.3.0 instead of this, you may define these options in `.mvn/maven.config` so the same config is used + # when running from the command line. + # `installAtEnd` and `deployAtEnd` are only effective with the recent version of the corresponding plugins. + MAVEN_CLI_OPTS: > + --batch-mode + --errors + --fail-at-end + --show-version + -DinstallAtEnd=true + -DdeployAtEnd=true + +# Check the list of available templates at https://gitlab.com/gitlab-org/gitlab/-/tree/master/lib/gitlab/ci/templates +include: + - template: Jobs/Secret-Detection.gitlab-ci.yml + - template: Workflows/MergeRequest-Pipelines.gitlab-ci.yml + +stages: + - build + - test + - deploy + - report + +.upstream-deploy-production-rules: + rules: + - if: $CI_PROJECT_NAMESPACE != "pmpl/examples" + when: never + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + when: always + allow_failure: true + +build: + stage: build + image: docker.io/library/maven:3.8.6-eclipse-temurin-17-focal + before_script: + - java -version && javac --version && mvn --version + - pwd + script: + - mvn $MAVEN_CLI_OPTS -DskipTests + -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository package + cache: + key: + files: + - pom.xml + paths: + - .m2/repository + artifacts: + paths: + - target/ + +test: + stage: test + image: docker.io/library/maven:3.8.6-eclipse-temurin-17-focal + needs: + - build + before_script: + - java -version && javac --version && mvn --version + - pwd + script: + # Run test suites and generate test reports + - mvn clean verify + # Get line coverage + - grep -o "Total[^%]*%" target/site/jacoco/index.html + coverage: '/Total.*?(\d{1,3})%/' + cache: + key: + files: + - pom.xml + paths: + - .m2/repository + artifacts: + paths: + - target/*.exec + - target/site/jacoco/ + reports: + junit: + - target/surefire-reports/TEST-*.xml + +deploy: + stage: deploy + image: docker.io/dokku/ci-docker-image:0.9.3 + rules: !reference [.upstream-deploy-production-rules, rules] + variables: + GIT_DEPTH: "0" + GIT_REMOTE_URL: "ssh://dokku@dokku-ppl.cs.ui.ac.id:22/spring-petclinic-rest" + SSH_PRIVATE_KEY: $PRODUCTION_SSH_PRIVATE_KEY + BRANCH: $CI_DEFAULT_BRANCH + script: + - dokku-deploy + after_script: + - dokku-unlock + environment: + name: production + url: https://spring-petclinic-rest.dokku-ppl.cs.ui.ac.id + needs: + - test + dependencies: [] + +visualize-coverage: + stage: report + image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.9 + before_script: [] + script: + - python /opt/cover2cover.py target/site/jacoco/jacoco.xml $CI_PROJECT_DIR/src/main/java > target/cobertura.xml + needs: + - test + dependencies: + - test + artifacts: + reports: + coverage_report: + coverage_format: cobertura + path: target/cobertura.xml + +sonarqube-check: + stage: report + image: docker.io/library/maven:3.8.6-eclipse-temurin-17-focal + variables: + SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" + GIT_DEPTH: "0" + rules: !reference [.upstream-deploy-production-rules, rules] + script: + - mvn -DskipTests verify sonar:sonar + cache: + key: "${CI_JOB_NAME}" + paths: + - .sonar/cache + +# TODO: Add manual CI job to re-deploy or re-create the deployed app on Dokku PPL diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..fbfaa878548d288f88d4ff7b6d24da39f0ec828a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +# Use JDK & Maven image to build the application +FROM docker.io/library/maven:3.9.4-eclipse-temurin-17-alpine AS builder + +# Set the working directory inside the container +WORKDIR /src + +# Copy the source code into the container +COPY . . + +# Build the application JAR file +RUN mvn -DskipTests package + +# Use JRE image for running the application +FROM docker.io/library/eclipse-temurin:17.0.8.1_1-jre-alpine + +# Create a non-root user named "app" to own and run the application +RUN addgroup app \ + && adduser -s /bin/false -G app -D -H app + +# Switch to the "app" user, so the application does not run as root +USER app + +# Set the working directory inside the container to /opt/app +WORKDIR /opt/app + +# Copy the app into the container +COPY --chown=app:app --from=builder /src/target/sitodo-*.jar . + +# Expose port 9966 +EXPOSE 9966 + +# Run the application JAR file +CMD ["/bin/sh", "-c", "java -jar spring-petclinic-rest-*.jar"] diff --git a/pom.xml b/pom.xml index c95b91e873bdd31cc8afebe8cabd43c4539decbf..bdb11afd031fd6945e0a2eb697ba9b7133d022b1 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ </parent> <properties> - <!-- Third librairies --> + <!-- Third-party libraries --> <spring-data-jdbc.version>1.2.1.RELEASE</spring-data-jdbc.version> <springdoc-openapi-ui.version>2.0.2</springdoc-openapi-ui.version> <jackson-databind-nullable.version>0.2.1</jackson-databind-nullable.version> @@ -29,11 +29,19 @@ <jacoco.version>0.8.8</jacoco.version> <openapi-generator-maven-plugin.version>6.3.0</openapi-generator-maven-plugin.version> <build-helper-maven-plugin.version>3.2.0</build-helper-maven-plugin.version> + <sonar-maven-plugin.version>3.9.1.2184</sonar-maven-plugin.version> <!-- Docker --> - <docker.jib-maven-plugin.version>1.3.0</docker.jib-maven-plugin.version> + <docker.jib-maven-plugin.version>3.4.0</docker.jib-maven-plugin.version> <docker.image.prefix>springcommunity</docker.image.prefix> <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version> + + <!-- Sonar Scanner --> + <sonar.host.url>https://sonarqube.cs.ui.ac.id</sonar.host.url> + <sonar.projectKey>pmpl_examples_spring-petclinic-rest_AYtlCaXx94kwVhMRxVzs</sonar.projectKey> + <sonar.coverage.jacoco.xmlReportPaths>target/site/jacoco/*.xml</sonar.coverage.jacoco.xmlReportPaths> + <sonar.junit.reportPaths>target/surefire-reports/*.xml</sonar.junit.reportPaths> + <sonar.qualitygate.wait>true</sonar.qualitygate.wait> </properties> <dependencies> @@ -339,6 +347,11 @@ </compilerArgs> </configuration> </plugin> + <plugin> + <groupId>org.sonarsource.scanner.maven</groupId> + <artifactId>sonar-maven-plugin</artifactId> + <version>${sonar-maven-plugin.version}</version> + </plugin> </plugins> </build> </project>