ci: add comprehensive GitLab CI with warnings-as-errors and optimal caching

Added new .gitlab-ci.yml with:

**Quality & Testing:**
- Compile job with --warnings-as-errors to catch all warnings
- Test job with coverage reporting
- Code quality checks (format, credo, dialyzer)
- PostgreSQL 17 service for tests

**Caching Strategy:**
- Hex/Mix packages cached by Elixir/OTP version
- Dependencies cached by mix.lock + branch
- Build artifacts cached and reused between jobs
- System packages (apt) cached to speed up dependency installation
- Separate cache update job for scheduled/lock file changes

**Pipeline Stages:**
1. Test: compile, test, quality checks (parallel)
2. Build: Docker image build and push to registry
3. Deploy: Manual deployment to production via kubectl

**Performance Improvements:**
- Jobs reuse compiled artifacts via GitLab cache
- System dependencies cached in apt-cache/
- Parallel execution of test, compile, and quality jobs
- Pull-only cache policy for most jobs (faster)

The existing .gitlab-ci.yml.nix remains for Nix-based builds if needed.
This commit is contained in:
Graham McIntire 2026-03-05 12:06:57 -06:00
parent 10e4690e97
commit 5356c2f37e
No known key found for this signature in database

182
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,182 @@
image: hexpm/elixir:1.19.5-erlang-28.0-debian-bookworm-20250113-slim
variables:
MIX_ENV: test
# Use GitLab's cache to speed up builds
MIX_HOME: $CI_PROJECT_DIR/.mix
HEX_HOME: $CI_PROJECT_DIR/.hex
stages:
- test
- build
- deploy
# Define cache configuration for maximum reuse
.mix_cache: &mix_cache
key:
files:
- mix.lock
prefix: $CI_COMMIT_REF_SLUG-mix
paths:
- deps/
- _build/test/
policy: pull
.hex_cache: &hex_cache
key:
prefix: hex-1.19.5-otp-28
paths:
- .hex/
- .mix/
policy: pull
# System dependencies cache
.system_cache: &system_cache
key:
prefix: system-deps-v1
paths:
- apt-cache/
policy: pull
before_script:
- apt-get update -qq
# Configure apt cache
- mkdir -p apt-cache
- export APT_CACHE_DIR=$CI_PROJECT_DIR/apt-cache
# Install system dependencies
- apt-get install -y -qq --no-install-recommends
libsnmp-dev snmp-mibs-downloader
build-essential git curl ca-certificates
-o dir::cache::archives=$APT_CACHE_DIR
# Install Hex and Rebar
- mix local.hex --force
- mix local.rebar --force
# Job to update caches (runs on schedule or manual trigger)
update_deps:
stage: test
cache:
- <<: *hex_cache
policy: pull-push
- <<: *mix_cache
policy: pull-push
- <<: *system_cache
policy: pull-push
script:
- mix deps.get
- mix deps.compile
- mix compile
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
changes:
- mix.lock
# Compile with warnings as errors
compile:
stage: test
cache:
- *hex_cache
- *mix_cache
- *system_cache
script:
- mix deps.get
- echo "Compiling with warnings as errors..."
- mix compile --warnings-as-errors
artifacts:
paths:
- _build/test/
expire_in: 1 hour
# Run tests
test:
stage: test
needs: [compile]
services:
- postgres:17-alpine
variables:
POSTGRES_DB: towerops_test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_HOST: postgres
cache:
- *hex_cache
- *mix_cache
- *system_cache
script:
- mix ecto.create
- mix ecto.migrate
- mix test --warnings-as-errors
coverage: '/\d+\.\d+\%\s+\|\s+Total/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
when: always
expire_in: 7 days
# Code quality checks
quality:
stage: test
needs: [compile]
cache:
- *hex_cache
- *mix_cache
- *system_cache
script:
- mix format --check-formatted
- mix credo --strict
- mix dialyzer
allow_failure: true # Don't block on dialyzer warnings
# Build Docker image (existing Nix build or standard build)
build:
stage: build
needs: [test, quality]
tags:
- docker
image: docker:24
services:
- docker:24-dind
before_script:
- echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
# Tag as latest for main branch
- |
if [ "$CI_COMMIT_BRANCH" = "main" ]; then
docker tag $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:latest
docker push $CI_REGISTRY_IMAGE:latest
fi
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_TAG
# Deploy to production
deploy:
stage: deploy
needs:
- job: build
artifacts: false
tags:
- home
image:
name: bitnami/kubectl:latest
entrypoint: [""]
script:
- kubectl config use-context towerops/towerops:home-cluster-agent
- DEPLOY_TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
- echo "Deploying at $DEPLOY_TIMESTAMP"
- kubectl set image deployment/towerops towerops=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -n towerops
- kubectl set env deployment/towerops DEPLOY_TIMESTAMP=$DEPLOY_TIMESTAMP -n towerops
environment:
name: production
kubernetes:
namespace: towerops
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual