stages: - build - test - deploy # Builds the Docker image and pushes it to the GitLab container registry on # port 443 (not 5050). Uses Docker-in-Docker (dind); the runner must have # privileged = true and no host docker.sock mounted into services. TLS must # stay off (DOCKER_TLS_CERTDIR: "") or the daemon listens on 2376 only. # Tagged with the commit SHA; :latest is also pushed on the default branch. build: stage: build image: docker:29.8.1 services: - docker:29.8.1-dind variables: DOCKER_HOST: tcp://docker:2375 DOCKER_DRIVER: overlay2 DOCKER_TLS_CERTDIR: "" rules: - if: $CI_PIPELINE_SOURCE == "push" script: - | echo "Waiting for dind daemon..." for i in $(seq 1 60); do if docker info >/dev/null 2>&1; then echo "dind daemon is up" break fi sleep 1 done - docker info # registry.gitlab.winsoo.org:443 flaps (~50% of connects time out from the # runner's network), so retry the registry operations. - | retry() { local n=1 max=6 delay=10 until "$@"; do if [ $n -ge $max ]; then echo "giving up after $n attempts" return 1 fi echo "attempt $n failed, retrying in ${delay}s..." n=$((n + 1)) sleep $delay done } - retry sh -c 'echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin "registry.gitlab.winsoo.org"' - docker build --pull -t "registry.gitlab.winsoo.org/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA" . - retry docker push "registry.gitlab.winsoo.org/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA" - | if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then docker tag "registry.gitlab.winsoo.org/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA" "registry.gitlab.winsoo.org/$CI_PROJECT_PATH:latest" retry docker push "registry.gitlab.winsoo.org/$CI_PROJECT_PATH:latest" fi after_script: - docker rmi "registry.gitlab.winsoo.org/$CI_PROJECT_PATH:$CI_COMMIT_SHORT_SHA" 2>/dev/null || true test: stage: test image: debian:13 services: - name: postgres:16 alias: preferences_db rules: - if: $CI_PIPELINE_SOURCE == "push" cache: key: pip paths: - .cache/pip variables: PIP_CACHE_DIR: $CI_PROJECT_DIR/.cache/pip # postgres service POSTGRES_USER: root POSTGRES_PASSWORD: ci-password POSTGRES_DB: preferences_db # app settings; dummy values only, real secrets never belong in a test job DEBUG: "true" DB_NAME: preferences_db DB_USER: root DB_PASSWORD: ci-password DB_HOST: preferences_db DB_PORT: "5432" BASE_OAUTH2_PROVIDER_PUBLIC_URL: https://accounts.invalid/oauth2 BASE_OAUTH2_PROVIDER_PRIVATE_URL: https://accounts.invalid/oauth2 CLIENT_ID: ci CLIENT_SECRET: ci SCOPES: "" before_script: # keep in sync with the Dockerfile - apt-get update - apt-get install -y python3 python3-pip binutils libproj-dev gdal-bin - pip3 install --break-system-packages --ignore-installed -r requirements.txt - pip3 install --break-system-packages setuptools script: - python3 manage.py test --noinput # Runs on the staging host through a shell runner tagged `staging` that can use # the docker CLI. The compose stack keeps its ./preferences checkout (mounted at # /app), so this updates that checkout and restarts the service. # Set DEPLOY_DIR in Settings > CI/CD > Variables (the directory that holds # docker-compose.yml and the ./preferences checkout). deploy_staging: stage: deploy tags: - staging rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: manual allow_failure: true # otherwise the pipeline shows "blocked" until someone deploys environment: name: staging script: - cd "$DEPLOY_DIR" - git -C preferences pull --ff-only origin "$CI_DEFAULT_BRANCH" - docker compose up -d --build preferences