stages: - validate - sonar - test - build variables: DENO_VERSION: "2.6.4" DOCKER_IMAGE_NAME: "$CI_REGISTRY/deeplite" PORT: "8000" # Global template for Deno jobs .deno_template: image: denoland/deno:${DENO_VERSION} before_script: - deno --version # ===== VALIDATION STAGE ===== fmt:check: extends: .deno_template stage: validate script: - deno fmt --check rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: "$CI_COMMIT_BRANCH" lint:check: extends: .deno_template stage: validate script: - deno lint rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: "$CI_COMMIT_BRANCH" type:check: extends: .deno_template stage: validate script: - deno check src/main.ts - deno check src/main.test.ts rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: "$CI_COMMIT_BRANCH" # ===== SONARQUBE STAGE ===== sonarqube:scan: stage: sonar image: name: sonarsource/sonar-scanner-cli:latest entrypoint: [""] variables: SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" GIT_DEPTH: "0" cache: key: "${CI_JOB_NAME}" paths: - .sonar/cache script: - | sonar-scanner \ -Dsonar.host.url="${SONAR_HOST}" \ -Dsonar.token="${SONAR_TOKEN}" \ -Dsonar.projectKey="${CI_PROJECT_PATH_SLUG}" \ -Dsonar.projectName="${CI_PROJECT_NAME}" \ -Dsonar.projectVersion="${CI_COMMIT_SHORT_SHA}" \ -Dsonar.sources=src \ -Dsonar.sourceEncoding=UTF-8 \ -Dsonar.language=ts \ -Dsonar.exclusions="**/*.test.ts,**/test/**" \ -Dsonar.tests=src \ -Dsonar.test.inclusions="**/*.test.ts" allow_failure: true rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: "$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH" dependencies: [] # ===== TEST STAGE ===== integration:test: extends: .deno_template stage: test variables: DEEPL_AUTH_KEY: "${DEEPL_AUTH_KEY}" BEARER_TOKEN: "${BEARER_TOKEN}" PORT: "8000" script: # Start server in background - echo "Starting server in background..." - deno run --allow-net --allow-env src/main.ts & - SERVER_PID=$! - echo "Server started with PID $SERVER_PID" # Wait for server to be ready (poll health endpoint) - echo "Waiting for server to be ready..." - | deno eval " for (let i = 1; i <= 30; i++) { try { await fetch('http://localhost:8000/health'); console.log('Server is ready!'); Deno.exit(0); } catch { console.log('Waiting for server... attempt ' + i + '/30'); await new Promise(r => setTimeout(r, 1000)); } } console.log('Server health check failed'); Deno.exit(1); " # Run tests - echo "Running integration tests..." - deno test --allow-net --allow-env src/main.test.ts # Cleanup: Kill the server - echo "Stopping server..." - kill $SERVER_PID || true - wait $SERVER_PID || true rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: "$CI_COMMIT_BRANCH" retry: max: 2 when: - runner_system_failure - stuck_or_timeout_failure # ===== BUILD & PUSH STAGE (Tags only) ===== docker:build-push: stage: build image: docker:29-dind services: - docker:29-dind variables: DOCKER_TLS_CERTDIR: "/certs" before_script: - docker info - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY" script: - export VERSION=${CI_COMMIT_TAG#v} - echo "Building and pushing Docker image version $VERSION" - | docker build \ --build-arg DENO_VERSION=${DENO_VERSION} \ --tag ${DOCKER_IMAGE_NAME}:${VERSION} \ --tag ${DOCKER_IMAGE_NAME}:${CI_COMMIT_SHA} \ --tag ${DOCKER_IMAGE_NAME}:latest \ . - docker push ${DOCKER_IMAGE_NAME}:${VERSION} - docker push ${DOCKER_IMAGE_NAME}:${CI_COMMIT_SHA} - docker push ${DOCKER_IMAGE_NAME}:latest - echo "Successfully pushed:" - echo " - ${DOCKER_IMAGE_NAME}:${VERSION}" - echo " - ${DOCKER_IMAGE_NAME}:${CI_COMMIT_SHA}" - echo " - ${DOCKER_IMAGE_NAME}:latest" rules: - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/' retry: max: 2 when: - runner_system_failure