diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..6994f62 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,157 @@ +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' diff --git a/Dockerfile.ci b/Dockerfile.ci new file mode 100644 index 0000000..5b0e97c --- /dev/null +++ b/Dockerfile.ci @@ -0,0 +1,7 @@ +FROM amazoncorretto:21-alpine +WORKDIR /app + +ARG JAR_FILE +COPY ${JAR_FILE} app.jar + +ENTRYPOINT ["java", "-jar", "app.jar"]