# GitLab CI/CD configuration for spens
#
# Pipeline:
#   1. lint     - run ruff on every commit
#   2. test     - run unit tests on every commit
#   3. build     - build sdist + wheel (on tags and default branch)
#   4. publish   - upload to the project's GitLab PyPI package registry (on tags)
#   5. release   - cut a GitLab Release for the tag, with artifacts attached (on tags)
#
# Requires Python >= 3.11 (see pyproject.toml).

stages:
  - test
  - build
  - publish
  - release

variables:
  UV_CACHE_DIR: ".uv-cache"
  # Tell uv/hatch not to try to write outside the project dir
  PIP_DISABLE_PIP_VERSION_CHECK: "1"

default:
  image: python:3.11-slim
  cache:
    key:
      files:
        - uv.lock
    paths:
      - .uv-cache/

.test-template:
  stage: test
  before_script:
    - pip install --quiet uv
    - uv sync --extra dev
  script:
    - uv run pytest tests/unit -v

lint:ruff:
  stage: test
  before_script:
    - pip install --quiet uv
    - uv sync --extra dev
  script:
    - uv run ruff check .

test:python3.11:
  extends: .test-template
  image: python:3.11-slim

test:python3.12:
  extends: .test-template
  image: python:3.12-slim
  allow_failure: true

build:
  stage: build
  rules:
    - if: $CI_COMMIT_TAG
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  before_script:
    - pip install --quiet build
  script:
    - python -m build
  artifacts:
    paths:
      - dist/
    expire_in: 1 week

# Publish to this project's GitLab PyPI package registry.
# Uses the built-in CI job token, so no extra credentials are needed.
# Consumers can install with:
#   pip install spens --index-url https://__token__:<personal-access-token>@gitlab.example.com/api/v4/projects/<project-id>/packages/pypi/simple
publish:gitlab-registry:
  stage: publish
  needs:
    - job: build
      artifacts: true
  rules:
    - if: $CI_COMMIT_TAG
  before_script:
    - pip install --quiet twine
  script:
    - >
      TWINE_PASSWORD=${CI_JOB_TOKEN}
      TWINE_USERNAME=gitlab-ci-token
      twine upload
      --repository-url "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi"
      --non-interactive
      dist/*

# Cut a GitLab Release for the tag once the package has been published.
# The built sdist/wheel are first uploaded to the generic package registry
# so they can be attached to the release as asset links.
release:gitlab:
  stage: release
  image: registry.gitlab.com/gitlab-org/release-cli:latest
  needs:
    - job: build
      artifacts: true
    - job: publish:gitlab-registry
  rules:
    - if: $CI_COMMIT_TAG
  before_script:
    - apk add --no-cache curl
  script:
    - |
      set -eu
      asset_flags=""
      for f in dist/*; do
        name="$(basename "$f")"
        url="${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/spens/${CI_COMMIT_TAG}/${name}"
        curl --fail --silent --show-error \
          --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
          --upload-file "$f" "${url}"
        asset_flags="${asset_flags} --assets-link {\"name\":\"${name}\",\"url\":\"${url}\"}"
      done
      # shellcheck disable=SC2086
      release-cli create \
        --server-url "${CI_SERVER_URL}" \
        --project-id "${CI_PROJECT_ID}" \
        --job-token "${CI_JOB_TOKEN}" \
        --name "spens ${CI_COMMIT_TAG}" \
        --description "Release ${CI_COMMIT_TAG}. The package is published to the project's PyPI registry; the sdist and wheel are attached below as assets." \
        --tag-name "${CI_COMMIT_TAG}" \
        ${asset_flags}

# Optional: publish to the public PyPI when a tag is pushed.
# Set a CI/CD variable (Settings > CI/CD > Variables) named PYPI_API_TOKEN
# (masked, protected) with your PyPI token, then uncomment this job.
#
# publish:pypi:
#   stage: publish
#   needs:
#     - job: build
#       artifacts: true
#   rules:
#     - if: $CI_COMMIT_TAG && $PYPI_API_TOKEN
#   before_script:
#     - pip install --quiet twine
#   script:
#     - >
#       TWINE_PASSWORD=${PYPI_API_TOKEN}
#       TWINE_USERNAME=__token__
#       twine upload --non-interactive dist/*
