From e657a4068dbddd0d8ad130547095e0114dd4362c Mon Sep 17 00:00:00 2001 From: Ricardo Campos Date: Sun, 22 Sep 2024 17:42:02 -0300 Subject: [PATCH] feat: add backend dockerfile and cloud native config --- .github/workflows/pr.yml | 29 +- java-api/Dockerfile | 32 ++ java-api/pom.xml | 290 +++++++++++++++++- .../java_api/config/CloudNativeConfig.java | 44 +++ .../config/HttpServletRequestRuntimeHint.java | 22 ++ .../src/main/resources/application.properties | 4 +- tools.md | 28 +- tools/check-backend.sh | 6 + 8 files changed, 449 insertions(+), 6 deletions(-) create mode 100644 java-api/Dockerfile create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/config/CloudNativeConfig.java create mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/config/HttpServletRequestRuntimeHint.java create mode 100755 tools/check-backend.sh diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 30e0006..357b688 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -100,4 +100,31 @@ jobs: run: cd java-api && ./mvnw --no-transfer-progress checkstyle:checkstyle -Dskip.checkstyle=false - name: All Tests - run: cd java-api && ./mvnw --no-transfer-progress clean verify -P all-tests --file pom.xml + run: cd java-api && ./mvnw --no-transfer-progress clean verify -P tests --file pom.xml + + java-docker-build: + name: Build Client Docker image + runs-on: ubuntu-latest + needs: + - client-code-checks + steps: + - uses: actions/checkout@v4 + - uses: docker/setup-buildx-action@v3 + name: Set up Docker Buildx + + - uses: docker/login-action@v3 + name: Login to GitHub Container Registry + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + push: true + context: ./java-api + tags: ghcr.io/ricardo-campos-org/react-typescript-todolist/java-api:${{ github.event.number }} + build-args: BUILD=${{ github.event.number }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/java-api/Dockerfile b/java-api/Dockerfile new file mode 100644 index 0000000..fefb283 --- /dev/null +++ b/java-api/Dockerfile @@ -0,0 +1,32 @@ +### Builder +FROM ghcr.io/graalvm/native-image:22.3.3 AS build + +# Copy +WORKDIR /app +COPY pom.xml mvnw ./ +COPY src ./src +COPY .mvn/ ./.mvn + +# Build +RUN ./mvnw -B package -Pnative -DskipTests + + +### Deployer +FROM gcr.io/distroless/java-base:nonroot AS deploy +ARG PORT=8585 + +# Receive build number as argument, retain as environment variable +ARG BUILD_NUMBER +ENV BUILD_NUMBER=${BUILD_NUMBER} + +# Copy +WORKDIR /app +COPY --from=build /app/target/tasknote-api ./tasknote-api + +# User, port and health check +USER 1001 +EXPOSE ${PORT} +HEALTHCHECK CMD timeout 10s bash -c 'true > /dev/tcp/127.0.0.1/8585' + +# Startup +ENTRYPOINT ["/app/tasknote-api", "-Dspring.profiles.active=prod"] diff --git a/java-api/pom.xml b/java-api/pom.xml index 4df81cb..c82c601 100644 --- a/java-api/pom.xml +++ b/java-api/pom.xml @@ -31,8 +31,70 @@ 17 + UTF-8 + UTF-8 + true + true + true + true + true + true + ${project.build.directory}/coverage-reports + ${maven.build.timestamp} + yyyy-MM-dd HH:mm:ss + + + + + tests + + tests + false + false + false + false + + + + + + prod + + prod + + + + + + native + + native + true + true + true + + + + + org.graalvm.buildtools + native-maven-plugin + + + build-native + + compile-no-fork + + package + + + + + + + + @@ -113,13 +175,11 @@ io.jsonwebtoken jjwt-impl 0.12.6 - runtime io.jsonwebtoken jjwt-jackson 0.12.6 - runtime @@ -161,6 +221,232 @@ + + org.apache.maven.plugins + maven-failsafe-plugin + 3.5.0 + + + integration-tests + + integration-test + verify + + + @{argLine} -Xmx1024m + false + ${skip.integration.tests} + + **/*IntegrationTest.java + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.5.0 + + @{argLine} -Xmx1024m + ${skip.unit.tests} + + **/*IntegrationTest.java + + + + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + ${jacoco.skip} + + **/config/** + **/dto/** + **/entity/** + **/exception/** + **/filter/** + **/interceptor/** + **/response/** + **/*$*Builder* + **/RestExceptionEndpoint.* + **/BackendStartApiApplication.* + + + + + pre-unit-test + + prepare-agent + + + ${jacoco.output.data}/jacoco-ut.exec + + + + post-unit-test + test + + report + + + ${jacoco.output.data}/jacoco-ut.exec + ${jacoco.output.data}/jacoco-ut + + + + pre-integration-test + pre-integration-test + + prepare-agent-integration + + + ${jacoco.output.data}/jacoco-it.exec + + + + post-integration-test + post-integration-test + + report-integration + + + ${jacoco.output.data}/jacoco-it.exec + ${jacoco.output.data}/jacoco-it + + + + merge-unit-and-integration + post-integration-test + + merge + + + + + ${jacoco.output.data} + + *.exec + + + + ${project.build.directory}/jacoco.exec + + + + merged-reports + post-integration-test + + report + + + ${project.build.directory}/jacoco.exec + ${jacoco.output.data}/merged-test-report + + + + coverage-check + + check + + + + + BUNDLE + + + LINE + COVEREDRATIO + 80% + + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.10.0 + + 17 + Javadoc Documentation for ${project.name} ${project.version} + ${project.name} ${project.version} + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.5.0 + + + default-cli + + enforce + + + + + + [3.2,) + Invalid Maven version. It should be at least 3.2 + + + 17 + Invalid Java Version. It should be at least 1.8 + + + + central + + + central + + + + No Snapshots Allowed in releases! + true + + + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.5.0 + + + com.puppycrawl.tools + checkstyle + 10.18.1 + + + + ${checkstyle.skip} + ${checkstyle.failsOnError} + + ${checkstyle.includeTestSourceDirectory} + + .mvn/google_checks.xml + true + false + + + + validate + checkstyle + + check + + + + diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/config/CloudNativeConfig.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/config/CloudNativeConfig.java new file mode 100644 index 0000000..90ff9df --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/config/CloudNativeConfig.java @@ -0,0 +1,44 @@ +package br.com.tasknoteapp.java_api.config; + +import br.com.tasknoteapp.java_api.request.LoginRequest; +import br.com.tasknoteapp.java_api.request.NotePatchRequest; +import br.com.tasknoteapp.java_api.request.NoteRequest; +import br.com.tasknoteapp.java_api.request.NoteUrlPatchRequest; +import br.com.tasknoteapp.java_api.request.TaskPatchRequest; +import br.com.tasknoteapp.java_api.request.TaskRequest; +import br.com.tasknoteapp.java_api.request.TaskUrlPatchRequest; +import br.com.tasknoteapp.java_api.response.JwtAuthenticationResponse; +import br.com.tasknoteapp.java_api.response.NoteResponse; +import br.com.tasknoteapp.java_api.response.NoteUrlResponse; +import br.com.tasknoteapp.java_api.response.SearchResponse; +import br.com.tasknoteapp.java_api.response.SummaryResponse; +import br.com.tasknoteapp.java_api.response.TaskResponse; +import br.com.tasknoteapp.java_api.response.TaskUrlResponse; +import br.com.tasknoteapp.java_api.response.UserResponse; +import org.springframework.aot.hint.annotation.RegisterReflectionForBinding; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportRuntimeHints; + +@Configuration +@RegisterReflectionForBinding({ + LoginRequest.class, + NotePatchRequest.class, + NoteRequest.class, + NoteUrlPatchRequest.class, + TaskPatchRequest.class, + TaskRequest.class, + TaskUrlPatchRequest.class, + JwtAuthenticationResponse.class, + NoteResponse.class, + NoteUrlResponse.class, + SearchResponse.class, + SummaryResponse.class, + TaskResponse.class, + TaskUrlResponse.class, + UserResponse.class, + io.jsonwebtoken.Claims.class, + io.jsonwebtoken.Jwts.class, + io.jsonwebtoken.impl.security.StandardSecureDigestAlgorithms.class, +}) +@ImportRuntimeHints(value = {HttpServletRequestRuntimeHint.class}) +public class CloudNativeConfig {} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/config/HttpServletRequestRuntimeHint.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/config/HttpServletRequestRuntimeHint.java new file mode 100644 index 0000000..f555cfe --- /dev/null +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/config/HttpServletRequestRuntimeHint.java @@ -0,0 +1,22 @@ +package br.com.tasknoteapp.java_api.config; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.aot.hint.ProxyHints; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; + +/** This class creates a RuntimeHint for the ServletRequest class. */ +public class HttpServletRequestRuntimeHint implements RuntimeHintsRegistrar { + + @Override + public void registerHints(@NonNull RuntimeHints hints, @Nullable ClassLoader classLoader) { + try { + ProxyHints proxies = hints.proxies(); + proxies.registerJdkProxy(HttpServletRequest.class); + } catch (Exception e) { + throw new RuntimeException("Could not register RuntimeHint: " + e.getMessage()); + } + } +} diff --git a/java-api/src/main/resources/application.properties b/java-api/src/main/resources/application.properties index 620ae87..37afea4 100644 --- a/java-api/src/main/resources/application.properties +++ b/java-api/src/main/resources/application.properties @@ -7,8 +7,8 @@ management.endpoint.health.show-details=always # Database, datasource and JPA spring.datasource.driver-class-name=org.postgresql.Driver -spring.datasource.url=jdbc:postgresql://${POSTGRES_HOST:localhost}:5432/${POSTGRES_DB:postgres} -spring.datasource.username=${POSTGRES_USER:postgres} +spring.datasource.url=jdbc:postgresql://${POSTGRES_HOST:localhost}:5432/${POSTGRES_DB:tasknote} +spring.datasource.username=${POSTGRES_USER:tasknoteuser} spring.datasource.password=${POSTGRES_PASSWORD:default} spring.jpa.properties.hibernate.default_schema=tasknote spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect diff --git a/tools.md b/tools.md index 45d08c8..cb0a68e 100644 --- a/tools.md +++ b/tools.md @@ -1,7 +1,7 @@ # Tools - **TaskNote:** https://tasknote.local -- **Docker Hub:** https://hub.docker.com/ +- **GitHub Container Registry:** https://ghcr.io/ - **Time tracking:** https://track.toggl.com/timer # Building locally @@ -22,3 +22,29 @@ Build and run with Docker Compose: ```sh docker compose --profile caddy up --build ``` + +# Interact with GitHub Package + +Login in: + +``` +export CR_PAT=YOUR_TOKEN +echo $CR_PAT | docker login ghcr.io -u RMCampos --password-stdin +``` + +Pull image: + +```sh +docker pull ghcr.io/ricardo-campos-org/react-typescript-todolist/client:50 +``` + +Run it: + +```sh +docker run -d --rm \ + -p 80:5000 \ + -e VITE_BUILD="client:50" \ + -e VITE_BACKEND_SERVER="http://localhost:8585" \ + --name tasknote \ + ghcr.io/ricardo-campos-org/react-typescript-todolist/client:50 +``` diff --git a/tools/check-backend.sh b/tools/check-backend.sh new file mode 100755 index 0000000..2b4eac9 --- /dev/null +++ b/tools/check-backend.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +cd java-api +./mvnw --no-transfer-progress clean compile -DskipTests && \ + ./mvnw --no-transfer-progress checkstyle:checkstyle -Dskip.checkstyle=false && \ + ./mvnw --no-transfer-progress clean verify -P tests --file pom.xml \ No newline at end of file