stages: - build - deploy # All jobs run on the project runner tagged "owseu runner" # (docker executor on the OWSEU workstation). default: tags: - owseu runner variables: PYTHON_VERSION: "3.12" SPHINX_OUTPUT_DIR: "public" DOCS_SOURCE_DIR: "docs/source" UV_CACHE_DIR: ".cache/uv" PIP_CACHE_DIR: ".cache/pip" # UV_LINK_MODE: "copy" # optional # Reusable snippet: apt + uv (no -y flag) .install-uv: &install-uv - | set -euo pipefail export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y --no-install-recommends curl ca-certificates git rm -rf /var/lib/apt/lists/* export PATH="$HOME/.local/bin:$PATH" if ! command -v uv >/dev/null 2>&1; then curl -fsSL https://astral.sh/uv/install.sh | sh fi uv --version # Optional: warm caches (no artifacts) install-deps: stage: build image: python:${PYTHON_VERSION} script: - *install-uv - | set -euo pipefail if [ -d .venv ]; then echo "Reusing cached virtual environment at .venv" else uv venv --python ${PYTHON_VERSION} .venv fi uv sync --group docs uv run python -c "import sys; print('Using', sys.executable)" cache: key: "uv${PYTHON_VERSION}-${CI_COMMIT_REF_SLUG}" paths: - .venv/ - .cache/uv/ - .cache/pip/ rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Build docs (reuses warmed caches) build-docs: stage: build image: python:${PYTHON_VERSION} needs: - job: install-deps before_script: - *install-uv - | set -euo pipefail if [ -d .venv ]; then echo "Reusing cached virtual environment at .venv" else uv venv --python ${PYTHON_VERSION} .venv fi uv sync --group docs uv run python --version script: - | set -euo pipefail uv run sphinx-build -b html "${DOCS_SOURCE_DIR}" "${SPHINX_OUTPUT_DIR}/latest" find "${SPHINX_OUTPUT_DIR}" -name '*.pyc' -delete cache: key: "uv${PYTHON_VERSION}-${CI_COMMIT_REF_SLUG}" policy: pull paths: - .venv/ - .cache/uv/ - .cache/pip/ artifacts: paths: - ${SPHINX_OUTPUT_DIR}/ expire_in: 1 week rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Deploy GitLab Pages pages: stage: deploy needs: - job: build-docs artifacts: true script: - | set -euo pipefail mkdir -p public cp -r "${SPHINX_OUTPUT_DIR}/latest/"* public/ artifacts: paths: - public rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Publish package to GitLab Package Registry (only on version tags) publish-package: stage: deploy image: python:${PYTHON_VERSION} before_script: - *install-uv script: - | set -euo pipefail uv venv --python ${PYTHON_VERSION} .venv uv build --no-sources --out-dir dist/ uv publish --index opencode dist/* variables: UV_PUBLISH_USERNAME: $CI_REGISTRY_USER UV_PUBLISH_PASSWORD: $CI_JOB_TOKEN rules: # Only run on semantic version tags like v4.0.0, v4.1.0-beta, etc. - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+/ # Build and push Docker image using Kaniko (doesn't require privileged mode) build-docker: stage: deploy image: name: gcr.io/kaniko-project/executor:debug entrypoint: [""] variables: REGISTRY_URL: ${CI_REGISTRY_IMAGE} script: - | # Extract version from tag (v4.0.0 -> 4.0.0) VERSION="${CI_COMMIT_TAG#v}" # Create Docker config for registry auth mkdir -p /kaniko/.docker echo "{\"auths\":{\"${CI_REGISTRY}\":{\"auth\":\"$(printf '%s:%s' "${CI_REGISTRY_USER}" "${CI_REGISTRY_PASSWORD}" | base64 | tr -d '\n')\"}}}" > /kaniko/.docker/config.json # Build and push with Kaniko /kaniko/executor \ --context "${CI_PROJECT_DIR}" \ --dockerfile "${CI_PROJECT_DIR}/Dockerfile" \ --destination "${CI_REGISTRY_IMAGE}:${VERSION}" \ --destination "${CI_REGISTRY_IMAGE}:latest" \ --cache=true echo "✅ Pushed Docker images:" echo " - ${CI_REGISTRY_IMAGE}:${VERSION}" echo " - ${CI_REGISTRY_IMAGE}:latest" rules: # Only run on semantic version tags like v4.0.0 - if: $CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+/