From 5a73be331be038adef9d64c0e6bcb4a4ab052505 Mon Sep 17 00:00:00 2001 From: Thatsaphorn Atchariyaphap Date: Thu, 29 May 2025 18:51:59 +0000 Subject: [PATCH] Gitlab CI/CD Pipeline --- .gitignore | 2 + .gitlab-ci-template.yml | 125 ++++++++++++++++++ .gitlab-ci.yml | 56 ++++++++ .run/DiscoveryServerApplication.run.xml | 15 --- .run/GatewayApplication.run.xml | 6 + .run/ServerApplication.run.xml | 7 + .run/npm-dev.run.xml | 4 +- backend/.gitlab-ci.yml | 48 +++++++ backend/Dockerfile.app | 32 +++++ backend/Dockerfile.base | 6 + backend/discovery/pom.xml | 32 ----- .../discovery/DiscoveryServerApplication.java | 19 --- .../src/main/resources/application.yml | 13 -- backend/entrypoint.sh | 26 ++++ backend/gateway/pom.xml | 45 ++++++- .../src/main/resources/application.yml | 7 +- backend/pom.xml | 1 - backend/server/pom.xml | 26 +++- .../contact/controller/ContactController.java | 2 +- .../{domain => }/model/ContactRequest.java | 2 +- .../{domain => }/model/ContactRequestDto.java | 2 +- .../{domain => }/model/HCaptchaConfig.java | 2 +- .../repository/ContactRequestsRepo.java | 2 +- .../contact/usecase/SubmitContactUseCase.java | 2 +- .../usecase/SubmitContactUseCaseImpl.java | 4 +- .../contact/util/HCaptchaValidator.java | 2 +- .../server/src/main/resources/application.yml | 23 ++-- docker-compose.yml | 24 ++++ frontend/.gitlab-ci.yml | 36 ++--- .../Contact/Section/ContactFormSection.tsx | 5 +- 30 files changed, 432 insertions(+), 144 deletions(-) create mode 100644 .gitlab-ci-template.yml create mode 100644 .gitlab-ci.yml delete mode 100644 .run/DiscoveryServerApplication.run.xml create mode 100644 backend/.gitlab-ci.yml create mode 100644 backend/Dockerfile.app create mode 100644 backend/Dockerfile.base delete mode 100644 backend/discovery/pom.xml delete mode 100644 backend/discovery/src/main/java/dev/rheinsw/discovery/DiscoveryServerApplication.java delete mode 100644 backend/discovery/src/main/resources/application.yml create mode 100644 backend/entrypoint.sh rename backend/server/src/main/java/dev/rheinsw/server/contact/{domain => }/model/ContactRequest.java (97%) rename backend/server/src/main/java/dev/rheinsw/server/contact/{domain => }/model/ContactRequestDto.java (89%) rename backend/server/src/main/java/dev/rheinsw/server/contact/{domain => }/model/HCaptchaConfig.java (88%) create mode 100644 docker-compose.yml diff --git a/.gitignore b/.gitignore index c6a6e71..971f1cf 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ /backend/discovery/target/ /backend/gateway/target/ /backend/server/target/ + +*.env diff --git a/.gitlab-ci-template.yml b/.gitlab-ci-template.yml new file mode 100644 index 0000000..70fe6c4 --- /dev/null +++ b/.gitlab-ci-template.yml @@ -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 + " diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..ad98c86 --- /dev/null +++ b/.gitlab-ci.yml @@ -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"' \ No newline at end of file diff --git a/.run/DiscoveryServerApplication.run.xml b/.run/DiscoveryServerApplication.run.xml deleted file mode 100644 index dfd80cc..0000000 --- a/.run/DiscoveryServerApplication.run.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - \ No newline at end of file diff --git a/.run/GatewayApplication.run.xml b/.run/GatewayApplication.run.xml index 1b53351..12fd0a8 100644 --- a/.run/GatewayApplication.run.xml +++ b/.run/GatewayApplication.run.xml @@ -1,6 +1,12 @@ + + +