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
2 changes: 2 additions & 0 deletions .github/scripts/extract_and_generate_html.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ replace_in_html_files() {
}

# Read the contents of lastRun.txt and sort it
pwd
tree .
last_runs=($(sort -n < loading/target/gatling/lastRun.txt))

# Create the HTML template
Expand Down
161 changes: 88 additions & 73 deletions .github/workflows/build-feature.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
steps:
# Checkout the code from the repository
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis

Expand All @@ -47,15 +47,15 @@ jobs:

# Cache Sonar packages to speed up the build process
- name: Cache SonarCloud packages
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar

# Cache Maven packages to speed up the build process
- name: Cache Maven packages
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
Expand All @@ -73,17 +73,38 @@ jobs:
mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=Raouf25_spring-boot-asynchronous-api
fi

docker:
name: Build & push to Docker Hub
needs: analyze_code
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t raouf25/spring-boot-asynchronous-api .
- name: Push to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Docker push
run: |
docker push raouf25/spring-boot-asynchronous-api:latest
docker tag raouf25/spring-boot-asynchronous-api registry.fly.io/spring-boot-asynchronous-api
docker images | grep "spring-boot-asynchronous"

deploy:
environment:
name: dev-swagger-ui
url: https://spring-boot-asynchronous-api.fly.dev/swagger-ui/index.html
name: Deploy app
needs: analyze_code
needs: docker
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
- run: |
flyctl deploy -i registry.fly.io/spring-boot-asynchronous-api

non_regression_test:
name: Non Regression Test
Expand All @@ -95,7 +116,7 @@ jobs:
steps:
# Checkout the code from the repository
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis

Expand All @@ -108,7 +129,7 @@ jobs:

# Cache Maven packages to speed up the build process
- name: Cache Maven packages
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
Expand All @@ -123,13 +144,13 @@ jobs:
# Create a "doc" directory and move karate reports to it
- name: Get report
run: |
mkdir -p doc
mv ./nrt/target/karate-reports/* ./doc
mkdir -p doc/karate
mv ./nrt/target/karate-reports/* ./doc/karate

- uses: actions/upload-artifact@master
- uses: actions/upload-artifact@v4
with:
name: karate
path: doc/karate
path: ./doc/karate/
# - name: Upload artifact
# uses: actions/upload-pages-artifact@v1
# with:
Expand All @@ -149,12 +170,20 @@ jobs:
steps:
# Checkout the code from the repository
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '21'

# Execute Gatling Test Suite
- name: Execute Gatling Test Suite
continue-on-error: true
run: |
mvn --version
java --version
mvn clean gatling:test --f loading/pom.xml

# New step to generate gatling-summary.html
Expand All @@ -163,65 +192,51 @@ jobs:
.github/scripts/extract_and_generate_html.sh
shell: bash

- uses: actions/upload-artifact@master
- uses: actions/upload-artifact@v4
with:
name: gatling
path: doc/gatling

# - name: Upload artifact
# uses: actions/upload-pages-artifact@v1
# with:
# # Upload entire repository
# path: './doc'
# - name: Deploy to GitHub Pages
# id: deployment
# uses: actions/deploy-pages@v1

# - name: Generate Job Summary
# uses: actions/github-script@v6
# with:
# script: |
# const fs = require('fs')
# const lastRuns = fs.readFileSync(`loading/target/gatling/lastRun.txt`).toString().trim().split('\n');
#
# for(const run of lastRuns) {
# const formattedLine = run.replace(/(.*)-(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, ' $1 at $2-$3-$4 $5:$6:$7');
# const results = JSON.parse(fs.readFileSync(`loading/target/gatling/${run}/js/stats.json`).toString());
# let tableContent = [
# [
# {data: 'Request', header: true},
# {data: 'Success ✅', header: true},
# {data: 'Errors ❌', header: true},
# {data: 'Min', header: true},
# {data: 'Max', header: true},
# {data: 'Avg.', header: true},
# {data: 'Std. Dev.', header: true},
# {data: 'RPS', header: true},
# ]
# ];
#
# for(const result in results.contents) {
# const requestMetrics = results.contents[result].stats;
# tableContent.push([
# requestMetrics.name,
# requestMetrics.numberOfRequests.ok.toString(),
# requestMetrics.numberOfRequests.ko.toString(),
# requestMetrics.minResponseTime.total.toString(),
# requestMetrics.maxResponseTime.total.toString(),
# requestMetrics.meanResponseTime.total.toString(),
# requestMetrics.standardDeviation.total.toString(),
# requestMetrics.meanNumberOfRequestsPerSecond.total.toString(),
# ]);
# }
#
# await core.summary
# .addHeading(`Results for ${formattedLine}`)
# .addTable(tableContent)
# .addQuote('All times are in millisecond (ms). RPS means "Requests per Second"')
# .write()
# }
#

- name: Generate Job Summary
uses: actions/github-script@v7
with:
script: |
const fs = require('fs')
const lastRuns = fs.readFileSync(`loading/target/gatling/lastRun.txt`).toString().trim().split('\n');
for(const run of lastRuns) {
const formattedLine = run.replace(/(.*)-(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/, ' $1 at $2-$3-$4 $5:$6:$7');
const results = JSON.parse(fs.readFileSync(`loading/target/gatling/${run}/js/stats.json`).toString());
let tableContent = [
[
{data: 'Request', header: true},
{data: 'Success ✅', header: true},
{data: 'Errors ❌', header: true},
{data: 'Min', header: true},
{data: 'Max', header: true},
{data: 'Avg.', header: true},
{data: 'Std. Dev.', header: true},
{data: 'RPS', header: true},
]
];
for(const result in results.contents) {
const requestMetrics = results.contents[result].stats;
tableContent.push([
requestMetrics.name,
requestMetrics.numberOfRequests.ok.toString(),
requestMetrics.numberOfRequests.ko.toString(),
requestMetrics.minResponseTime.total.toString(),
requestMetrics.maxResponseTime.total.toString(),
requestMetrics.meanResponseTime.total.toString(),
requestMetrics.standardDeviation.total.toString(),
requestMetrics.meanNumberOfRequestsPerSecond.total.toString(),
]);
}
await core.summary
.addHeading(`Results for ${formattedLine}`)
.addTable(tableContent)
.addQuote('All times are in millisecond (ms). RPS means "Requests per Second"')
.write()
}
deploy_reports:
name: Deploy Reports
needs:
Expand All @@ -230,16 +245,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4

- name: Retrieve saved Non Regression Test
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: karate
path: doc/karate
path: ./doc/karate

- name: Retrieve saved Loading Test
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: gatling
path: doc/gatling
Expand All @@ -251,7 +266,7 @@ jobs:
path: './doc'
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v1
uses: actions/deploy-pages@v3

- name: Echo URL Reports
run: |
Expand Down
84 changes: 14 additions & 70 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,77 +1,21 @@
#
# Build stage
#
FROM maven:3.9.5-eclipse-temurin-21-alpine AS MAVEN_BUILD
COPY pom.xml /build/
COPY app /build/app/
WORKDIR /build/
RUN mvn -f /build/app/pom.xml clean package
# ----------------
# Using Oracle GraalVM for JDK 21
FROM container-registry.oracle.com/graalvm/native-image:21-ol8 AS builder

#
# Package stage
#
# base image to build a JRE
FROM eclipse-temurin:21-alpine AS deps
# Set the working directory to /home/app
WORKDIR /build

# Identify dependencies
COPY --from=MAVEN_BUILD ./build/app/target/*-SNAPSHOT.jar /app/app.jar
RUN mkdir /app/unpacked && \
cd /app/unpacked && \
unzip ../app.jar && \
cd .. && \
$JAVA_HOME/bin/jdeps \
--ignore-missing-deps \
--print-module-deps \
-q \
--recursive \
--multi-release 17 \
--class-path="./unpacked/BOOT-INF/lib/*" \
--module-path="./unpacked/BOOT-INF/lib/*" \
./app.jar > /deps.info
# Copy the source code into the image for building
COPY . /build

# base image to build a JRE
FROM eclipse-temurin:21-alpine AS custome-jre-step
# Build
RUN ./mvnw --no-transfer-progress native:compile -Pnative -DskipTests -f ./app/pom.xml

# required for strip-debug to work
RUN apk add --no-cache binutils
# The deployment Image
FROM container-registry.oracle.com/os/oraclelinux:8-slim

# copy module dependencies info
COPY --from=deps /deps.info /deps.info

# Build small JRE image
RUN $JAVA_HOME/bin/jlink \
--verbose \
--add-modules $(cat /deps.info) \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /customjre

# main app image
FROM alpine:latest
ENV JAVA_HOME=/jre
ENV PATH="${JAVA_HOME}/bin:${PATH}"

# copy JRE from the base image
COPY --from=custome-jre-step /customjre $JAVA_HOME

# Add app user
ARG APPLICATION_USER=appuser
RUN adduser --no-create-home -u 1000 -D $APPLICATION_USER

# Configure working directory
RUN mkdir /app && \
chown -R $APPLICATION_USER /app

# Set the working directory
WORKDIR /app

# Copy the built JAR file from the build stage
COPY --chown=1000:1000 --from=MAVEN_BUILD /build/app/target/spring-boot-asynchronous-api-app-*.jar /app/app.jar

# Expose port 8080
EXPOSE 8080

# Run the JAR file as the entrypoint
ENTRYPOINT [ "/jre/bin/java", "-XX:+UseSerialGC","-Xss512k", "-jar", "/app/app.jar" ]
# Copy the native executable into the containers
COPY --from=builder /build/app/target/spring-boot-asynchronous-api-app app
ENTRYPOINT ["/app"]
1 change: 1 addition & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ For more information, please refer to the :
- [jacoco-multi-module-sample](https://medium.com/javarevisited/merging-integration-unit-and-functional-test-reports-with-jacoco-de5cde9b56e1)
- [jacoco and sonar](https://www.baeldung.com/sonarqube-jacoco-code-coverage)
- [gatling loading test](https://github.com/krizsan/gatling-examples)
- [gatling loading test project](https://github.com/Automation-Test-Starter/gatling-maven-scala-demo)

---------------
## Project Building Steps:
Expand Down
3 changes: 1 addition & 2 deletions app/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,13 @@
<artifactId>spring-boot-asynchronous-api-app</artifactId>

<properties>

<java.version>21</java.version>
<!-- SonarCloud -->
<sonar.organization>raouf25-github</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
<sonar.coverage.jacoco.xmlReportPaths> ${project.basedir}/report-aggregate/target/site/jacoco-aggregate/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>

</properties>

<dependencies>
Expand Down
Loading