Gitlab CI/CD Pipeline

This commit is contained in:
2025-05-29 18:51:59 +00:00
parent cb4eb80105
commit 5a73be331b
30 changed files with 432 additions and 144 deletions

2
.gitignore vendored
View File

@@ -7,3 +7,5 @@
/backend/discovery/target/
/backend/gateway/target/
/backend/server/target/
*.env

125
.gitlab-ci-template.yml Normal file
View File

@@ -0,0 +1,125 @@
.docker-login-template:
before_script:
- |
echo "Logging into Docker..."
echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
.image-tag-template:
script: |
TAG="${CI_COMMIT_REF_NAME//\//_}"
DOCKER_IMAGE="$CI_REGISTRY/$CI_PROJECT_PATH/$IMAGE_NAME"
echo "Generated tag: $TAG"
echo "Docker image: $DOCKER_IMAGE:$TAG"
.docker-build-template:
extends: .docker-login-template
stage: dockerize
image: docker:20.10@sha256:2967f0819c84dd589ed0a023b9d25dcfe7a3c123d5bf784ffbb77edf55335f0c
script:
- !reference [ .image-tag-template, script ]
- |
echo "Building Docker image for $IMAGE_NAME in $WORKDIR_PATH"
cd $WORKDIR_PATH
BUILD_ARGS="--build-arg IMAGE_TAG=$TAG"
if [ -n "$COMMON_IMAGE" ]; then BUILD_ARGS="$BUILD_ARGS --build-arg COMMON_IMAGE=$COMMON_IMAGE:$TAG"; fi
if [ -n "$BUILD_FOLDER" ]; then BUILD_ARGS="$BUILD_ARGS --build-arg BUILD_FOLDER=$BUILD_FOLDER"; fi
if [ -n "$IMAGE_NAME" ]; then BUILD_ARGS="$BUILD_ARGS --build-arg IMAGE_NAME=$IMAGE_NAME"; fi
if [ -n "$MAIN_CLASS" ]; then BUILD_ARGS="$BUILD_ARGS --build-arg MAIN_CLASS=$MAIN_CLASS"; fi
docker build $BUILD_ARGS -t $DOCKER_IMAGE:$TAG -f $DOCKERFILE_PATH .
if [[ "$TAG" == "dev" || "$TAG" == "production" || "$TAG" == "pipeline" ]]; then
echo "Pushing Docker image $DOCKER_IMAGE:$TAG"
docker push $DOCKER_IMAGE:$TAG
# After pushing the image
DIGEST=$(docker inspect --format='{{index .RepoDigests 0}}' $DOCKER_IMAGE:$TAG | cut -d '@' -f2)
echo "$DIGEST" > "$CI_PROJECT_DIR/digest-${IMAGE_NAME}.txt"
echo "Digest for $IMAGE_NAME: $DIGEST"
else
echo "Skipping push for non-dev/non-production branch: $TAG"
fi
artifacts:
paths:
- digest-*.txt
expire_in: 1 hour
# BUILD COMMON IMAGE
.docker-common-template:
extends: .docker-login-template
stage: docker-base
image: docker:20.10@sha256:2967f0819c84dd589ed0a023b9d25dcfe7a3c123d5bf784ffbb77edf55335f0c
script:
- !reference [ .image-tag-template, script ]
- |
echo "Building BASE Docker image for $IMAGE_NAME..."
cd $WORKDIR_PATH
docker build -f $DOCKERFILE_PATH -t $DOCKER_IMAGE:$TAG .
if [[ "$TAG" == "dev" || "$TAG" == "production" || "$TAG" == "pipeline" ]]; then
echo "Pushing Docker image $DOCKER_IMAGE:$TAG"
docker push $DOCKER_IMAGE:$TAG
else
echo "Skipping push for non-dev/non-production branch: $TAG"
fi
# Deployment
.install-deploy-key: &install-deploy-key
- |
echo "Installing SSH deploy key..."
which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )
mkdir -p ~/.ssh
echo "$DEPLOY_KEY_BASE64" | base64 -d > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/deploy_key
if [[ "$TAG" == "dev" || "$TAG" == "pipeline" ]]; then
HOST="$DEPLOY_DEV_HOST"
PORT="${DEPLOY_DEV_PORT:-22}"
else
HOST="$DEPLOY_PROD_HOST"
PORT="${DEPLOY_PROD_PORT:-22}"
fi
echo "Scanning SSH host $HOST on port $PORT"
ssh-keyscan -p "$PORT" "$HOST" >> ~/.ssh/known_hosts || true
.deploy-template:
stage: deploy
image: docker:20.10@sha256:2967f0819c84dd589ed0a023b9d25dcfe7a3c123d5bf784ffbb77edf55335f0c
before_script:
- !reference [ .install-deploy-key ]
script: |
if [[ "$TAG" == "dev" || "$TAG" == "pipeline" ]]; then
HOST="$DEPLOY_DEV_HOST"
PORT="${DEPLOY_DEV_PORT:-22}"
else
HOST="$DEPLOY_PROD_HOST"
PORT="${DEPLOY_PROD_PORT:-22}"
fi
echo "Image digests:"
echo " gateway : $(cat digest-gateway.txt)"
echo " server : $(cat digest-server.txt)"
echo " frontend : $(cat digest-frontend.txt)"
echo "Injecting image digests"
cp docker-compose.yml docker-compose.generated.yml
sed -i "s|registry.boomlab.party/rheinsw/rheinsw-mono-repo/gateway|registry.boomlab.party/rheinsw/rheinsw-mono-repo/gateway@$(cat digest-gateway.txt)|g" docker-compose.generated.yml
sed -i "s|registry.boomlab.party/rheinsw/rheinsw-mono-repo/server|registry.boomlab.party/rheinsw/rheinsw-mono-repo/server@$(cat digest-server.txt)|g" docker-compose.generated.yml
sed -i "s|registry.boomlab.party/rheinsw/rheinsw-mono-repo/frontend|registry.boomlab.party/rheinsw/rheinsw-mono-repo/frontend@$(cat digest-frontend.txt)|g" docker-compose.generated.yml
echo "Copying docker-compose.generated.yml to $HOST:$REMOTE_ENV_PATH/docker-compose.yml"
# Ensure remote path exists before scp
ssh -p "$PORT" "$DEPLOY_USER@$HOST" "mkdir -p $REMOTE_ENV_PATH"
# Copy
scp -P "$PORT" docker-compose.generated.yml "$DEPLOY_USER@$HOST:$REMOTE_ENV_PATH/docker-compose.yml"
echo "Deploying on $HOST"
ssh -p "$PORT" "$DEPLOY_USER@$HOST" "
cd $REMOTE_ENV_PATH
echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" -u "$CI_REGISTRY_USER" --password-stdin
docker compose down || true
docker compose pull || true
docker compose up -d
"

56
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,56 @@
image: docker:20.10@sha256:2967f0819c84dd589ed0a023b9d25dcfe7a3c123d5bf784ffbb77edf55335f0c
include:
- local: '.gitlab-ci-template.yml'
- local: 'backend/.gitlab-ci.yml'
- local: 'frontend/.gitlab-ci.yml'
stages:
- build
- docker-base
- dockerize
- deploy
- sync
variables:
CI_REGISTRY_IMAGE: $CI_REGISTRY/$CI_PROJECT_PATH
PROJECT_NAME: $CI_PROJECT_NAME
# Next JS vars
NEXT_PUBLIC_ENV: "production"
OUTPUT_DIR: ".next"
.deploy_production_rule: &deploy_production_rule
- if: $CI_COMMIT_BRANCH == "production"
when: manual
allow_failure: true
sync_dev_branch:
stage: sync
image: node:22@sha256:f6b9c31ace05502dd98ef777aaa20464362435dcc5e312b0e213121dcf7d8b95
rules:
- if: '$CI_COMMIT_BRANCH == "production"'
before_script:
- git config --global user.email "gitlab-ci@rhein-software.dev"
- git config --global user.name "GitLab CI"
script:
- git remote set-url origin "https://oauth2:${CI_JOB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git"
- git checkout dev
- git pull origin dev
- git merge --no-ff origin/production
- git push origin dev
deploy_dev:
extends: .deploy-template
variables:
TAG: '$CI_COMMIT_BRANCH'
REMOTE_ENV_PATH: /rheinsw/dev
rules:
- if: '$CI_COMMIT_BRANCH == "dev" || $CI_COMMIT_BRANCH == "pipeline"'
deploy_prod:
extends: .deploy-template
variables:
TAG: production
REMOTE_ENV_PATH: /rheinsw/prod
rules:
- if: '$CI_COMMIT_BRANCH == "production"'

View File

@@ -1,15 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="DiscoveryServerApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
<module name="discovery" />
<option name="SPRING_BOOT_MAIN_CLASS" value="dev.rheinsw.discovery.DiscoveryServerApplication" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="dev.rheinsw.discovery.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -1,6 +1,12 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="GatewayApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
<option name="envFilePaths">
<option value="$PROJECT_DIR$/gateway.env" />
</option>
<module name="gateway" />
<selectedOptions>
<option name="environmentVariables" />
</selectedOptions>
<option name="SPRING_BOOT_MAIN_CLASS" value="dev.rheinsw.gateway.GatewayApplication" />
<extension name="coverage">
<pattern>

View File

@@ -1,7 +1,14 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ServerApplication" type="SpringBootApplicationConfigurationType" factoryName="Spring Boot" nameIsGenerated="true">
<option name="envFilePaths">
<option value="$PROJECT_DIR$/server.env" />
</option>
<module name="server" />
<selectedOptions>
<option name="environmentVariables" />
</selectedOptions>
<option name="SPRING_BOOT_MAIN_CLASS" value="dev.rheinsw.server.ServerApplication" />
<option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/backend/server" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="dev.rheinsw.server.*" />

View File

@@ -6,7 +6,9 @@
<script value="dev" />
</scripts>
<node-interpreter value="project" />
<envs />
<envs>
<env name="HCAPTCHA_SECRET" value="10000000-ffff-ffff-ffff-000000000001" />
</envs>
<method v="2" />
</configuration>
</component>

48
backend/.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,48 @@
build_backend:
stage: build
image: maven:3.9.9-eclipse-temurin-21@sha256:2e3824afeb41f61761adee95318814e6669bd59aaf61255b2af47064b8755c02
script:
- cd backend
- mvn package
artifacts:
paths:
- backend/common/target/
- backend/gateway/target/
- backend/discovery/target/
- backend/server/target
expire_in: 1 hour
docker_common:
extends: .docker-common-template
variables:
IMAGE_NAME: common
WORKDIR_PATH: backend
DOCKERFILE_PATH: Dockerfile.base
needs:
- build_backend
docker_gateway:
extends: .docker-build-template
variables:
IMAGE_NAME: gateway
COMMON_IMAGE: "$CI_REGISTRY/$CI_PROJECT_PATH/common"
WORKDIR_PATH: backend
DOCKERFILE_PATH: Dockerfile.app
BUILD_FOLDER: "gateway/target"
MAIN_CLASS: dev.rheinsw.gateway.GatewayApplication
needs:
- build_backend
- docker_common
docker_server:
extends: .docker-build-template
variables:
IMAGE_NAME: server
COMMON_IMAGE: "$CI_REGISTRY/$CI_PROJECT_PATH/common"
WORKDIR_PATH: backend
DOCKERFILE_PATH: Dockerfile.app
BUILD_FOLDER: "server/target"
MAIN_CLASS: dev.rheinsw.server.ServerApplication
needs:
- build_backend
- docker_common

32
backend/Dockerfile.app Normal file
View File

@@ -0,0 +1,32 @@
# Dockerfile.app
ARG COMMON_IMAGE
FROM ${COMMON_IMAGE}
ARG BUILD_FOLDER
ARG MAIN_CLASS
ARG IMAGE_NAME
ARG IMAGE_TAG
ENV BUILD_FOLDER=${BUILD_FOLDER}
ENV MAIN_CLASS=${MAIN_CLASS}
ENV IMAGE_NAME=${IMAGE_NAME}
ENV IMAGE_TAG=${IMAGE_TAG}
# Log ARGs at build time
RUN echo "BUILD_FOLDER: ${BUILD_FOLDER}" && \
echo "MAIN_CLASS: ${MAIN_CLASS}" && \
echo "IMAGE_NAME: ${IMAGE_NAME}" && \
echo "IMAGE_TAG: ${IMAGE_TAG}"
WORKDIR /app
# Copy artifacts (relative to /app)
COPY ${BUILD_FOLDER}/${IMAGE_NAME}-*.jar app.jar
COPY ${BUILD_FOLDER}/libs/ libs/
# Copy and set entrypoint (relative to /app)
COPY entrypoint.sh entrypoint.sh
RUN chmod +x entrypoint.sh
ENTRYPOINT ["./entrypoint.sh"]

6
backend/Dockerfile.base Normal file
View File

@@ -0,0 +1,6 @@
# Dockerfile.base
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY common/target/common-*.jar libs/common.jar

View File

@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>dev.rheinsw</groupId>
<artifactId>backend</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>discovery</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,19 +0,0 @@
package dev.rheinsw.discovery;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author Bummsa / BoomerHD / Thatsaphorn Atchariyaphap
* @since 21.04.25
*/
@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServerApplication {
public static void main(String[] args) {
SpringApplication.run(DiscoveryServerApplication.class, args);
}
}

View File

@@ -1,13 +0,0 @@
server:
port: 8761
spring:
application:
name: discovery-server
eureka:
client:
register-with-eureka: false
fetch-registry: false
server:
wait-time-in-ms-when-sync-empty: 0

26
backend/entrypoint.sh Normal file
View File

@@ -0,0 +1,26 @@
#!/bin/sh
echo "---------------------------"
echo "Starting Java Application"
echo "Main class : ${MAIN_CLASS}"
echo "App JAR : app.jar"
echo "Libs folder : libs/"
echo "Detected JARs in libs/:"
find libs -type f -name "*.jar" -exec echo " -> {}" \;
if [ ! -f app.jar ]; then
echo "ERROR: app.jar not found. Ensure it is copied into the container."
exit 1
fi
# Build classpath string
CLASSPATH="app.jar"
for jar in libs/*.jar; do
CLASSPATH="$CLASSPATH:$jar"
done
echo "---------------------------"
echo "Executing: java -cp \"$CLASSPATH\" $MAIN_CLASS"
echo "---------------------------"
exec java -cp "$CLASSPATH" "$MAIN_CLASS"

View File

@@ -18,14 +18,55 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/libs</outputDirectory>
<includeScope>runtime</includeScope>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.2.0</version>
</dependency>
<!-- Tools -->

View File

@@ -1,11 +1,6 @@
server:
port: 8080
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
spring:
application:
name: gateway
@@ -16,7 +11,7 @@ spring:
gateway:
routes:
- id: server
uri: lb://server
uri: http://localhost:8081
predicates:
- Path=/api/**
filters:

View File

@@ -11,7 +11,6 @@
<modules>
<module>common</module>
<module>discovery</module>
<module>gateway</module>
<module>server</module>
</modules>

View File

@@ -36,6 +36,28 @@
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/libs</outputDirectory>
<includeScope>runtime</includeScope>
</configuration>
</execution>
</executions>
</plugin>
<!-- <plugin>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-maven-plugin</artifactId>-->
<!-- </plugin>-->
</plugins>
</build>
@@ -45,10 +67,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>

View File

@@ -1,6 +1,6 @@
package dev.rheinsw.server.contact.controller;
import dev.rheinsw.server.contact.domain.model.ContactRequestDto;
import dev.rheinsw.server.contact.model.ContactRequestDto;
import dev.rheinsw.server.contact.usecase.SubmitContactUseCase;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;

View File

@@ -1,4 +1,4 @@
package dev.rheinsw.server.contact.domain.model;
package dev.rheinsw.server.contact.model;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;

View File

@@ -1,4 +1,4 @@
package dev.rheinsw.server.contact.domain.model;
package dev.rheinsw.server.contact.model;
import dev.rheinsw.shared.transport.Dto;

View File

@@ -1,4 +1,4 @@
package dev.rheinsw.server.contact.domain.model;
package dev.rheinsw.server.contact.model;
import lombok.Getter;
import lombok.Setter;

View File

@@ -1,6 +1,6 @@
package dev.rheinsw.server.contact.repository;
import dev.rheinsw.server.contact.domain.model.ContactRequest;
import dev.rheinsw.server.contact.model.ContactRequest;
import org.springframework.data.jpa.repository.JpaRepository;
/**

View File

@@ -1,6 +1,6 @@
package dev.rheinsw.server.contact.usecase;
import dev.rheinsw.server.contact.domain.model.ContactRequestDto;
import dev.rheinsw.server.contact.model.ContactRequestDto;
import org.springframework.http.ResponseEntity;
/**

View File

@@ -1,7 +1,7 @@
package dev.rheinsw.server.contact.usecase;
import dev.rheinsw.server.contact.domain.model.ContactRequest;
import dev.rheinsw.server.contact.domain.model.ContactRequestDto;
import dev.rheinsw.server.contact.model.ContactRequest;
import dev.rheinsw.server.contact.model.ContactRequestDto;
import dev.rheinsw.server.contact.repository.ContactRequestsRepo;
import dev.rheinsw.server.contact.util.HCaptchaValidator;
import dev.rheinsw.server.mail.domain.MailRequest;

View File

@@ -1,6 +1,6 @@
package dev.rheinsw.server.contact.util;
import dev.rheinsw.server.contact.domain.model.HCaptchaConfig;
import dev.rheinsw.server.contact.model.HCaptchaConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

View File

@@ -1,13 +1,13 @@
server:
port: 0 # random port
port: 8081
spring:
application:
name: server
datasource:
url: jdbc:postgresql://localhost:5432/rheinsw_dev
username: rheinsw
password: rheinsw
url: jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
hibernate:
@@ -20,8 +20,8 @@ spring:
mail:
host: smtp.resend.com
port: 587
username: resend
password: re_JnLD5ndg_GnKtXcTqskXm1bg7Wxnghna3
username: ${MAIL_USERNAME}
password: ${MAIL_PASSWORD}
properties:
mail:
smtp:
@@ -30,15 +30,10 @@ spring:
enable: true
default-encoding: UTF-8
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
hcaptcha:
secret: ES_ff59a664dc764f92870bf2c7b4eab7c5
secret: ${HCAPTCHA_SECRET}
logging:
level:
org.hibernate.SQL: DEBUG
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
org.hibernate.SQL: ${LOG_SQL_LEVEL}
org.hibernate.type.descriptor.sql.BasicBinder: ${LOG_BINDER_LEVEL}

24
docker-compose.yml Normal file
View File

@@ -0,0 +1,24 @@
services:
gateway:
image: registry.boomlab.party/rheinsw/rheinsw-mono-repo/gateway
container_name: gateway
ports:
- "8080:8080"
env_file:
- ./gateway.env
server:
image: registry.boomlab.party/rheinsw/rheinsw-mono-repo/server
container_name: server
ports:
- "8081:8081"
env_file:
- ./server.env
frontend:
image: registry.boomlab.party/rheinsw/rheinsw-mono-repo/frontend
container_name: frontend
env_file:
- ./frontend.env
ports:
- "5100:3000"

View File

@@ -1,35 +1,23 @@
stages:
- frontend_build
- dockerize
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .next/cache/
variables:
NEXT_PUBLIC_ENV: "production"
OUTPUT_DIR: ".next"
PROJECT_NAME: $CI_PROJECT_NAME
DOCKER_IMAGE: $CI_REGISTRY_IMAGE
frontend_build_job:
stage: frontend_build
build_frontend:
stage: build
image: node:22@sha256:f6b9c31ace05502dd98ef777aaa20464362435dcc5e312b0e213121dcf7d8b95
script:
- cd frontend
- npm install
- npx next build
- npm run lint
- |
cd frontend
npm install
npx next build
dockerize_frontend:
stage: dockerize
extends: .docker_build_template
docker_frontend:
extends: .docker-build-template
variables:
DOCKER_CONTEXT: frontend
DOCKERFILE: Dockerfile
DOCKER_IMAGE: $DOCKER_IMAGE
DOCKER_BUILD_ARGS: ""
IMAGE_NAME: frontend
WORKDIR_PATH: frontend
DOCKERFILE_PATH: Dockerfile
needs:
- job: frontend_build_job
- build_frontend

View File

@@ -22,10 +22,7 @@ const ContactFormSection = () => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const isDev = process.env.NODE_ENV === "development";
const hCaptchaSiteKey = isDev
? "10000000-ffff-ffff-ffff-000000000001" // hCaptcha test sitekey
: "ES_ff59a664dc764f92870bf2c7b4eab7c5";
const hCaptchaSiteKey: string = process.env.NEXT_PUBLIC_HCAPTCHA_SITE_KEY ?? "null";
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
setForm({...form, [e.target.name]: e.target.value});