stages: - package - build-image - push - deploy variables: # ---------- Gradle ---------- GRADLE_IMAGE: gradle:8.14.4-jdk21 GRADLE_USER_HOME: ${CI_PROJECT_DIR}/.gradle # ---------- Docker ---------- DOCKER_IMAGE: docker:27.5.1 DOCKER_SERVICE: docker:27.5.1-dind DOCKER_HOST: tcp://docker:2375 DOCKER_TLS_CERTDIR: "" # ---------- Application ---------- APP_NAME: delta-force-guide-server # ---------- Image tags ---------- IMAGE_TAG: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA} LATEST_TAG: ${CI_REGISTRY_IMAGE}:latest # ---------- CI Dockerfile ---------- CI_DOCKERFILE: Dockerfile.ci cache: key: ${CI_COMMIT_REF_SLUG} paths: - .gradle/wrapper - .gradle/caches policy: pull-push # ==================================================================== # Reusable template for Docker jobs # ==================================================================== .docker: image: ${DOCKER_IMAGE} services: - name: ${DOCKER_SERVICE} command: ["--tls=false"] variables: DOCKER_HOST: tcp://docker:2375 DOCKER_TLS_CERTDIR: "" # Trigger the pipeline for MRs, the default branch, and tags workflow: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - if: '$CI_COMMIT_TAG' # ==================================================================== # Stage 1 — Package: build the JAR with Gradle # ==================================================================== package: stage: package image: ${GRADLE_IMAGE} script: - ./gradlew build artifacts: name: "${CI_JOB_NAME}-${CI_COMMIT_SHORT_SHA}" paths: - build/libs/*.jar expire_in: 1 hour # ==================================================================== # Stage 2 — Build Docker image using the pre-built JAR artifact # ==================================================================== build-image: stage: build-image extends: .docker script: # Resolve the actual JAR path - JAR_FILE=$(ls build/libs/delta-force-guide-server-*.jar | head -1) - echo "Packaging JAR: ${JAR_FILE}" # Build image with the CI-specific single-stage Dockerfile - | docker build \ --build-arg JAR_FILE="${JAR_FILE}" \ -f ${CI_DOCKERFILE} \ -t ${IMAGE_TAG} \ -t ${LATEST_TAG} \ . # Save the image as a CI artefact for the next stage - docker save ${IMAGE_TAG} ${LATEST_TAG} > image.tar artifacts: paths: - image.tar expire_in: 1 hour needs: - package # ==================================================================== # Stage 3 — Push image to GitLab Container Registry # ==================================================================== push: stage: push extends: .docker script: - docker load < image.tar - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY} - docker push ${IMAGE_TAG} - docker push ${LATEST_TAG} needs: - build-image rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' - if: '$CI_COMMIT_TAG' # ==================================================================== # Stage 4 — Deploy on the target server via SSH # ==================================================================== deploy: stage: deploy image: alpine:latest before_script: - apk add --no-cache openssh-client - eval "$(ssh-agent -s)" - echo "${DEPLOY_SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add - - mkdir -p ~/.ssh - chmod 700 ~/.ssh script: - | ssh -o StrictHostKeyChecking=no ${DEPLOY_USER}@${DEPLOY_HOST} " set -e echo '=== Pulling image ===' echo ${CI_REGISTRY_PASSWORD} | docker login -u ${CI_REGISTRY_USER} --password-stdin ${CI_REGISTRY} docker pull ${IMAGE_TAG} echo '=== Stopping old container ===' docker stop ${APP_NAME} || true docker rm ${APP_NAME} || true echo '=== Starting new container ===' docker run -d \ --name ${APP_NAME} \ --restart unless-stopped \ -p ${DEPLOY_PORT:-8080}:8080 \ ${IMAGE_TAG} echo '=== Cleaning up old images ===' docker image prune -f echo '=== Deployment complete ===' " needs: - push environment: name: production url: http://${DEPLOY_HOST}:${DEPLOY_PORT:-8080} rules: - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'