Commit a60750d5 authored by Gaëtan Montury's avatar Gaëtan Montury
Browse files

feat: add Zensical build system support as alternative to MkDocs

parent 1e0d9d7f
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
# GitLab CI template for MkDocs

This project implements a GitLab CI/CD template to build your static website with [MkDocs](https://www.mkdocs.org/).
Since version 2.11, this template also supports [Zensical](https://zensical.org/docs/get-started/). This tool is highly compatible with MkDocs, particularly Material for MkDocs
, as it was created by the same author.

## Usage

@@ -42,15 +44,22 @@ The MkDocs template uses some global configuration used throughout all jobs.

| Input / Variable | Description                            | Default value     |
| ----------------------- | -------------------------------------- | ----------------- |
| `image` / `MKD_IMAGE` | The Docker image used to run MkDocs    | `docker.io/squidfunk/mkdocs-material:latest` <br/>[![Trivy Badge](https://to-be-continuous.gitlab.io/doc/secu/trivy-badge-MKD_IMAGE.svg)](https://to-be-continuous.gitlab.io/doc/secu/trivy-MKD_IMAGE) |
| `image` / `MKD_IMAGE` | The Docker image used to run MkDocs or Zensical  (see below) | `docker.io/squidfunk/mkdocs-material:latest` <br/>[![Trivy Badge](https://to-be-continuous.gitlab.io/doc/secu/trivy-badge-MKD_IMAGE.svg)](https://to-be-continuous.gitlab.io/doc/secu/trivy-MKD_IMAGE) |
| `build-system` / `MKD_BUILD_SYSTEM` | The engine to use: `mkdocs`, `zensical` or `auto` to detect the available tool (see below) | `auto` |
| `workspace-dir` / `MKD_WORKSPACE_DIR` | MkDocs sources directory | `.` |
| `requirements-file` / `MKD_REQUIREMENTS_FILE` | Requirements file. If the file is not found in the repository, requirements are read from the `MKD_REQUIREMENTS` variable | `requirements.txt` |
| `requirements` / `MKD_REQUIREMENTS` | Space separated requirements (ignored if a requirement file is found) | `mkdocs` |
| `requirements-file` / `MKD_REQUIREMENTS_FILE` | Requirements file. If the file is not found in the repository, requirements are read from the `MKD_REQUIREMENTS` variable <br/>Not supported by Zensical (see below) | `requirements.txt` |
| `requirements` / `MKD_REQUIREMENTS` | Space separated requirements (ignored if a requirement file is found)<br/>Not supported by Zensical (see below) | `mkdocs` |
| `site-dir`/ `MKD_SITE_DIR`          | MkDocs generated site directory (relative to `$MKD_WORKSPACE_DIR`), the path will be declared as artifact | `site` |
| `prebuild-script` / `MKD_PREBUILD_SCRIPT` | Pre-build hook script | `mkdocs-pre-build.sh` |
| `PIP_INDEX_URL` | Python repository url | _none_ |
| `pip-opts` / `PIP_OPTS` | pip extra [options](https://pip.pypa.io/en/stable/cli/pip/#general-options) | _none_ |

:information_source: `MKD_IMAGE`: To use Zensical, you can use `docker.io/squidfunk/mkdocs-material:latest`, or any image with Python (`docker.io/library/python:latest`) or Astral uv (`docker.io/astral/uv:python3.14-trixie-slim`).

:information_source: `MKDOCS_BUILD_SYSTEM`: When using Zensical, you can specify the build tool version by including a [version identifier](https://peps.python.org/pep-0440/). For example: `MKDOCS_BUILD_SYSTEM="zensical==0.0.10"`.

:warning: Unlike MkDocs, Zensical doesn't require managing Python dependencies. While it currently lacks a component system, most features are built-in.

## Jobs

### `mkdocs` job
+8 −1
Original line number Diff line number Diff line
@@ -9,9 +9,16 @@
  "variables": [
    {
      "name": "MKD_IMAGE",
      "description": "The Docker image used to run MkDocs",
      "description": "The Docker image used to run MkDocs or Zensical",
      "default": "docker.io/squidfunk/mkdocs-material:latest"
    },
    {
      "name": "MKD_BUILD_SYSTEM",
      "description": "The engine to use: `mkdocs`, `zensical` or `auto` to detect the available tool",
      "type": "enum",
      "values": ["auto", "mkdocs", "zensical"],
      "default": "auto"
    },
    {
      "name": "MKD_BUILD_ARGS",
      "description": "Arguments used by the build job",
+93 −4
Original line number Diff line number Diff line
@@ -17,8 +17,15 @@
spec:
  inputs:
    image:
      description: The Docker image used to run MkDocs
      description: The Docker image used to run MkDocs or Zensical
      default: docker.io/squidfunk/mkdocs-material:latest
    build-system:
      description: "The engine to use: `mkdocs`, `zensical` or `auto` to detect the available tool"
      options:
      - auto
      - mkdocs
      - zensical
      default: auto
    build-args:
      description: Arguments used by the build job
      default: ''
@@ -116,6 +123,7 @@ workflow:
variables:
  MKD_WORKSPACE_DIR: $[[ inputs.workspace-dir ]]
  MKD_IMAGE: $[[ inputs.image ]]
  MKD_BUILD_SYSTEM: $[[ inputs.build-system ]]
  MKD_REQUIREMENTS: $[[ inputs.requirements ]]
  MKD_REQUIREMENTS_FILE: $[[ inputs.requirements-file ]]
  MKD_SITE_DIR: $[[ inputs.site-dir ]]
@@ -372,6 +380,40 @@ stages:
    done
  }

  function guess_mkdocs_engine() {
    case "${MKD_BUILD_SYSTEM:-auto}" in
    auto)
      ;;
    mkdocs*)
      export MKD_BUILD_SYSTEM_CMD="mkdocs"
      log_info "--- Docs system explicitly declared: ${MKD_BUILD_SYSTEM} cmd=\\e[33m$MKD_BUILD_SYSTEM_CMD\\e[0m"
      return
      ;;
    zensical*)
      export MKD_BUILD_SYSTEM_CMD="zensical"
      log_info "--- Docs system explicitly declared: ${MKD_BUILD_SYSTEM} cmd=\\e[33m$MKD_BUILD_SYSTEM_CMD\\e[0m"
      return
      ;;
    *)
      log_warn "--- Unknown declared docs system: \\e[33;1m${MKD_BUILD_SYSTEM}\\e[0m: please read template doc"
      ;;
    esac

    if command -v zensical > /dev/null
    then
      MKD_BUILD_SYSTEM="zensical"
      MKD_BUILD_SYSTEM_CMD="zensical"
      log_info "--- Zensical found: use as docs engine"
    elif command -v mkdocs > /dev/null
    then
      MKD_BUILD_SYSTEM="mkdocs"
      MKD_BUILD_SYSTEM_CMD="mkdocs"
      log_info "--- MkDocs found: use as docs engine"
    else
      log_error "--- No docs system detected, please use an image with MkDocs or Zensical"
    fi
  }

  function prepare_mkdocs() {
    if [[ -f "${MKD_REQUIREMENTS_FILE}" ]]; then
      log_info "installing requirements from file ${MKD_REQUIREMENTS_FILE}"
@@ -405,6 +447,41 @@ stages:
    fi
  }

  function prepare_zensical() {
    if [[ "${MKD_REQUIREMENTS_FILE}"  ]] || [[ "${MKD_REQUIREMENTS}" ]]; then
      log_warn "--- Custom requirements detected but unsupported by Zensical"
    fi

    if [[ -f "${MKD_PREBUILD_SCRIPT}" ]]; then
      log_info "--- \\e[32mpre-build hook\\e[0m (\\e[33;1m${MKD_PREBUILD_SCRIPT}\\e[0m) found: execute"
      chmod +x "${MKD_PREBUILD_SCRIPT}"
      "./${MKD_PREBUILD_SCRIPT}"
    fi

    if ! command -v zensical > /dev/null; then
      if command -v uv > /dev/null; then
        MKD_BUILD_SYSTEM_CMD="uv tool run --from ${MKD_BUILD_SYSTEM} ${MKD_BUILD_SYSTEM_CMD}"
      elif command -v pip > /dev/null; then
        # shellcheck disable=SC2086
        pip install ${MKD_BUILD_SYSTEM} ${PIP_OPTS} 
        MKD_BUILD_SYSTEM_CMD="zensical"
      fi
    fi

    cfg_file="mkdocs.yml"
    if [[ -f "mkdocs.yaml" ]]; then
      cfg_file="mkdocs.yaml"
    fi
    if ! grep -e '^site_url:' "$cfg_file"; then
      log_info "Update $cfg_file with site_url to ${CI_PAGES_URL}"
      echo "site_url: ${CI_PAGES_URL}" >> "$cfg_file"
    fi
    if ! grep -e '^repo_url:' "$cfg_file"; then
      log_info "Update $cfg_file with repo_url to ${CI_PROJECT_URL}"
      echo "repo_url: ${CI_PROJECT_URL}" >> "$cfg_file"
    fi
  }

  unscope_variables
  eval_all_secrets

@@ -426,7 +503,10 @@ mkdocs:
  extends: .mkdocs-base
  stage: build
  variables:
    # set local cache dir; most Python tools honour XDG specs
    XDG_CACHE_HOME: "$CI_PROJECT_DIR/.cache"
    PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
    UV_CACHE_DIR: "$CI_PROJECT_DIR/.cache/uv"
  # Cache downloaded dependencies and plugins between builds.
  # To keep cache across branches add 'key: "$CI_JOB_NAME"'
  cache:
@@ -434,11 +514,20 @@ mkdocs:
    when: always
    paths:
      - .cache
      # - $CI_PROJECT_DIR/.venv
  script:
    - prepare_mkdocs
    - mkdocs build ${MKD_BUILD_ARGS}
    - guess_mkdocs_engine
    - |
      if [[ ${MKD_BUILD_SYSTEM} == "mkdocs" ]]; then
        prepare_mkdocs
      fi
    - |
      if [[ ${MKD_BUILD_SYSTEM} == "zensical" ]]; then
        prepare_zensical
      fi
    - $MKD_BUILD_SYSTEM_CMD build ${MKD_BUILD_ARGS}
  artifacts:
    name: "mkdocs build from $CI_COMMIT_REF_SLUG"
    name: "docs build from $CI_COMMIT_REF_SLUG"
    paths:
      - ${MKD_WORKSPACE_DIR}/${MKD_SITE_DIR}
    expire_in: 1 day