From dd6bb2502cea02941b6ecd694c56da861ca67ba2 Mon Sep 17 00:00:00 2001 From: "Francesca.L.Bleken@sintef.no" Date: Wed, 3 Jun 2026 10:27:11 +0200 Subject: [PATCH 1/4] Print out more info about sphinx run --- .github/workflows/cd_docs.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cd_docs.yml b/.github/workflows/cd_docs.yml index d4ad4670d..6cbec85e9 100644 --- a/.github/workflows/cd_docs.yml +++ b/.github/workflows/cd_docs.yml @@ -5,6 +5,7 @@ on: push: branches: - master + - ghpagesfail release: types: - published @@ -77,7 +78,9 @@ jobs: cmake .. -DFORCE_EXAMPLES=ON -DWITH_DOC=ON - name: Run MakeFile - run: make + run: | + set -euo pipefail + make VERBOSE=1 2>&1 | tee make-docs.log working-directory: ${{ env.BUILD_DIR }} - name: Verify Python AutoAPI output @@ -112,6 +115,12 @@ jobs: if [[ "${BEST_AUTOAPI_INDEX_COUNT}" -lt 1 ]]; then echo "ERROR: AutoAPI output was not found in any docs HTML directory." + echo "---- Make/Sphinx log tail ----" + if [[ -f "${BUILD_DIR}/make-docs.log" ]]; then + tail -n 300 "${BUILD_DIR}/make-docs.log" || true + else + echo "No ${BUILD_DIR}/make-docs.log found." + fi echo "---- All AutoAPI directories under workspace ----" find . -type d -path '*/autoapi' | sort || true echo "---- All AutoAPI index files under workspace ----" From 508834577d97426b5af0982160086c1142abe86b Mon Sep 17 00:00:00 2001 From: "Francesca.L.Bleken@sintef.no" Date: Wed, 3 Jun 2026 10:35:33 +0200 Subject: [PATCH 2/4] Try with python 3.12 for docs --- .github/workflows/cd_docs.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cd_docs.yml b/.github/workflows/cd_docs.yml index 6cbec85e9..e83c51136 100644 --- a/.github/workflows/cd_docs.yml +++ b/.github/workflows/cd_docs.yml @@ -37,7 +37,8 @@ jobs: - name: Setup Python uses: actions/setup-python@v6 with: - python-version: "3.13.5" + # Keep docs on a stable interpreter for current Sphinx/AutoAPI stack. + python-version: "3.12" - name: Install system dependencies run: | From 62de4526e774e060d251d24035d1a94a310f2a12 Mon Sep 17 00:00:00 2001 From: "Francesca.L.Bleken@sintef.no" Date: Wed, 3 Jun 2026 10:51:54 +0200 Subject: [PATCH 3/4] Try some conf changes --- doc/conf.py.in | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/doc/conf.py.in b/doc/conf.py.in index 052addb1b..d39122ec5 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -7,6 +7,7 @@ import os import sys from pathlib import Path import re +import shutil from typing import TYPE_CHECKING from subprocess import call @@ -77,25 +78,32 @@ if plugin_root.exists(): if plugin_dir.is_dir() ] -# Prefer the built Python package tree (stable module namespace), but fall -# back to source tree if docs are built without package generation. +# Stage API sources into the docs build tree and point AutoAPI there. +# This avoids CI path/readability issues when mixing source and build trees. autoapi_build_dir = Path("@CMAKE_BINARY_DIR@/bindings/python/dlite") autoapi_source_dir = Path("@CMAKE_SOURCE_DIR@/bindings/python") +autoapi_stage_root = Path(__file__).resolve().parent / "_autoapi_sources" +autoapi_stage_pkg = autoapi_stage_root / "dlite" -def _has_python_sources(path: Path) -> bool: - if not path.exists() or not path.is_dir(): - return False - return any(path.rglob("*.py")) or any(path.rglob("*.pyi")) - -# Prefer the source tree for deterministic docs in CI. The build tree can -# exist but still be incomplete depending on build order/configuration. -autoapi_dirs = [] -if _has_python_sources(autoapi_source_dir): - autoapi_dirs.append(str(autoapi_source_dir)) -if _has_python_sources(autoapi_build_dir): - autoapi_dirs.append(str(autoapi_build_dir)) -if not autoapi_dirs: - autoapi_dirs = [str(autoapi_source_dir)] +if autoapi_stage_root.exists(): + shutil.rmtree(autoapi_stage_root) +autoapi_stage_pkg.mkdir(parents=True, exist_ok=True) + +for source_file in sorted(autoapi_source_dir.glob("*.py")): + if source_file.name == "doxy2swig.py": + continue + shutil.copy2(source_file, autoapi_stage_pkg / source_file.name) + +# Keep generated package entry points when available. +for generated_name in ("__init__.py", "paths.py"): + generated_path = autoapi_build_dir / generated_name + if generated_path.exists(): + shutil.copy2(generated_path, autoapi_stage_pkg / generated_name) + +if not (autoapi_stage_pkg / "__init__.py").exists(): + (autoapi_stage_pkg / "__init__.py").write_text('"""DLite Python API."""\n', encoding="utf-8") + +autoapi_dirs = [str(autoapi_stage_root)] print(f"[conf.py] AutoAPI dirs: {autoapi_dirs}") autoapi_type = "python" From 8ee0b807cb6e10045b4cc35061a1535690ea06ad Mon Sep 17 00:00:00 2001 From: "Francesca.L.Bleken@sintef.no" Date: Wed, 3 Jun 2026 15:36:57 +0200 Subject: [PATCH 4/4] Try out --- .github/docker/Dockerfile-cd-docs-local | 24 +++++ .github/docker/cd_docs_apt_packages.txt | 19 ++++ .../run_cd_docs_ci_local_in_container.sh | 36 ++++++++ .github/workflows/cd_docs.yml | 91 +------------------ README.md | 25 +++++ tools/cd_docs_build_and_verify.sh | 84 +++++++++++++++++ tools/run_cd_docs_ci_local.sh | 78 ++++++++++++++++ 7 files changed, 271 insertions(+), 86 deletions(-) create mode 100644 .github/docker/Dockerfile-cd-docs-local create mode 100644 .github/docker/cd_docs_apt_packages.txt create mode 100755 .github/docker/run_cd_docs_ci_local_in_container.sh create mode 100755 tools/cd_docs_build_and_verify.sh create mode 100755 tools/run_cd_docs_ci_local.sh diff --git a/.github/docker/Dockerfile-cd-docs-local b/.github/docker/Dockerfile-cd-docs-local new file mode 100644 index 000000000..8bd13f1c6 --- /dev/null +++ b/.github/docker/Dockerfile-cd-docs-local @@ -0,0 +1,24 @@ +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive + +COPY .github/docker/cd_docs_apt_packages.txt /tmp/cd_docs_apt_packages.txt + +RUN apt-get update --fix-missing && \ + xargs -a /tmp/cd_docs_apt_packages.txt apt-get install -y --no-install-recommends && \ + rm -rf /var/lib/apt/lists/* /tmp/cd_docs_apt_packages.txt + +WORKDIR /workspace + +COPY requirements.txt requirements_doc.txt requirements_dev.txt ./ + +RUN python3 -m venv /opt/venv +ENV PATH="/opt/venv/bin:${PATH}" + +RUN pip install --upgrade pip && \ + pip install -r requirements.txt -r requirements_doc.txt -r requirements_dev.txt + +COPY .github/docker/run_cd_docs_ci_local_in_container.sh /usr/local/bin/run_cd_docs_ci_local_in_container.sh +RUN chmod +x /usr/local/bin/run_cd_docs_ci_local_in_container.sh + +ENTRYPOINT ["/usr/local/bin/run_cd_docs_ci_local_in_container.sh"] diff --git a/.github/docker/cd_docs_apt_packages.txt b/.github/docker/cd_docs_apt_packages.txt new file mode 100644 index 000000000..e4b15be4d --- /dev/null +++ b/.github/docker/cd_docs_apt_packages.txt @@ -0,0 +1,19 @@ +ca-certificates +cmake +doxygen +g++ +gcc +git +graphviz +libhdf5-dev +libxml2-dev +libxslt-dev +make +python3 +python3-dev +python3-numpy +python3-pip +python3-venv +python3-yaml +rsync +swig diff --git a/.github/docker/run_cd_docs_ci_local_in_container.sh b/.github/docker/run_cd_docs_ci_local_in_container.sh new file mode 100755 index 000000000..36a01863a --- /dev/null +++ b/.github/docker/run_cd_docs_ci_local_in_container.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +BUILD_DIR="tmp" + +cd /workspace + +if [[ -n "${CI_SOURCE_REF:-}" ]]; then + echo "== Source ref requested: ${CI_SOURCE_REF} ==" +fi +if [[ -n "${CI_SOURCE_SHA:-}" ]]; then + echo "== Expected source SHA: ${CI_SOURCE_SHA} ==" +fi +if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + ACTUAL_SHA="$(git rev-parse HEAD)" + echo "== Container source SHA: ${ACTUAL_SHA} ==" + if [[ -n "${CI_SOURCE_SHA:-}" ]] && [[ "${ACTUAL_SHA}" != "${CI_SOURCE_SHA}" ]]; then + echo "ERROR: Container source SHA does not match expected SHA." + exit 1 + fi +fi + +echo "== Tool versions ==" +python --version +swig -version | head -n 1 || true +cmake --version | head -n 1 +doxygen --version +dot -V + +echo "== Build and verify docs (single source of truth) ==" +if [[ ! -x tools/cd_docs_build_and_verify.sh ]]; then + chmod +x tools/cd_docs_build_and_verify.sh +fi +BUILD_DIR="${BUILD_DIR}" tools/cd_docs_build_and_verify.sh + +echo "== SUCCESS: Local cd_docs reproduction completed ==" diff --git a/.github/workflows/cd_docs.yml b/.github/workflows/cd_docs.yml index e83c51136..57afec8e0 100644 --- a/.github/workflows/cd_docs.yml +++ b/.github/workflows/cd_docs.yml @@ -33,6 +33,7 @@ jobs: uses: actions/checkout@v6 with: fetch-depth: 0 + clean: true - name: Setup Python uses: actions/setup-python@v6 @@ -43,17 +44,7 @@ jobs: - name: Install system dependencies run: | sudo apt-get update --fix-missing - sudo apt-get install \ - libxml2-dev \ - libxslt-dev \ - libhdf5-dev \ - doxygen \ - graphviz \ - python3 \ - python3-pip \ - python3-dev \ - python3-numpy \ - python3-yaml + xargs -a .github/docker/cd_docs_apt_packages.txt sudo apt-get install -y #python3 -m pip install psycopg2-binary==2.9.5 - name: Install Python dependencies @@ -69,82 +60,10 @@ jobs: doxygen --version dot -V - - name: Run CMAKE + - name: Build and verify docs (single source of truth) run: | - rm -rf "${BUILD_DIR}" - mkdir -p "${BUILD_DIR}" - cd "${BUILD_DIR}" - Python3_ROOT=$(python3 -c 'import sys; print(sys.exec_prefix)') \ - CFLAGS='-Wno-missing-field-initializers' \ - cmake .. -DFORCE_EXAMPLES=ON -DWITH_DOC=ON - - - name: Run MakeFile - run: | - set -euo pipefail - make VERBOSE=1 2>&1 | tee make-docs.log - working-directory: ${{ env.BUILD_DIR }} - - - name: Verify Python AutoAPI output - run: | - set -euo pipefail - mapfile -t DOCS_CANDIDATES < <(find . -type f -path '*/doc/html/index.html' -not -path './gh-pages/*' | sed 's#/index.html$##' | sort -u) - if [[ "${#DOCS_CANDIDATES[@]}" -eq 0 ]]; then - echo "ERROR: Could not locate Sphinx HTML output directory (*/doc/html)." - echo "---- Candidate doc directories ----" - find . -maxdepth 4 -type d -name doc | sort || true - echo "---- Candidate html directories ----" - find . -maxdepth 6 -type d -name html | sort || true - exit 1 - fi - - DOCS_HTML_DIR="" - AUTOAPI_DIR="" - BEST_AUTOAPI_INDEX_COUNT=-1 - for CANDIDATE_DIR in "${DOCS_CANDIDATES[@]}"; do - CANDIDATE_AUTOAPI_DIR="${CANDIDATE_DIR}/autoapi" - CANDIDATE_AUTOAPI_INDEX_COUNT=$(find "${CANDIDATE_AUTOAPI_DIR}" -type f -name index.html 2>/dev/null | wc -l) - echo "Candidate docs HTML directory: ${CANDIDATE_DIR} (AutoAPI index pages: ${CANDIDATE_AUTOAPI_INDEX_COUNT})" - if [[ "${CANDIDATE_AUTOAPI_INDEX_COUNT}" -gt "${BEST_AUTOAPI_INDEX_COUNT}" ]]; then - BEST_AUTOAPI_INDEX_COUNT="${CANDIDATE_AUTOAPI_INDEX_COUNT}" - DOCS_HTML_DIR="${CANDIDATE_DIR}" - AUTOAPI_DIR="${CANDIDATE_AUTOAPI_DIR}" - fi - done - - echo "Using docs HTML directory: ${DOCS_HTML_DIR}" - echo "DOCS_HTML_DIR=${DOCS_HTML_DIR}" >> "$GITHUB_ENV" - - if [[ "${BEST_AUTOAPI_INDEX_COUNT}" -lt 1 ]]; then - echo "ERROR: AutoAPI output was not found in any docs HTML directory." - echo "---- Make/Sphinx log tail ----" - if [[ -f "${BUILD_DIR}/make-docs.log" ]]; then - tail -n 300 "${BUILD_DIR}/make-docs.log" || true - else - echo "No ${BUILD_DIR}/make-docs.log found." - fi - echo "---- All AutoAPI directories under workspace ----" - find . -type d -path '*/autoapi' | sort || true - echo "---- All AutoAPI index files under workspace ----" - find . -type f -path '*/autoapi/*/index.html' | sort | head -n 200 || true - echo "---- Sphinx config excerpt ----" - find . -type f -path '*/doc/_build/conf.py' -print -exec sed -n '1,260p' {} \; | grep -E 'autoapi_(dirs|ignore|file_patterns|python_use_implicit_namespaces)' || true - echo "---- docs HTML listing ----" - ls -la "${DOCS_HTML_DIR}" || true - exit 1 - fi - - echo "Using AutoAPI directory: ${AUTOAPI_DIR}" - AUTOAPI_INDEX_COUNT="${BEST_AUTOAPI_INDEX_COUNT}" - echo "Detected ${AUTOAPI_INDEX_COUNT} AutoAPI index page(s)." - - if [[ ! -f "${DOCS_HTML_DIR}/api.html" ]] || ! grep -Eq 'autoapi/.*/index.html' "${DOCS_HTML_DIR}/api.html"; then - echo "ERROR: API landing page does not link to Python AutoAPI pages." - echo "---- api.html excerpt ----" - sed -n '460,620p' "${DOCS_HTML_DIR}/api.html" || true - exit 1 - fi - - find "${AUTOAPI_DIR}" -maxdepth 3 -type f -name index.html | sort | head -n 20 + chmod +x tools/cd_docs_build_and_verify.sh + BUILD_DIR="${BUILD_DIR}" tools/cd_docs_build_and_verify.sh - name: Determine deployment target run: | diff --git a/README.md b/README.md index 05b6c731a..03abb583a 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,31 @@ Documentation ------------- The official documentation for DLite can be found on https://sintef.github.io/dlite/. +Reproduce docs CI locally (Docker) +---------------------------------- +To reproduce the `cd_docs.yml` docs build and AutoAPI checks locally in a +Docker container, run: + +```shell +./tools/run_cd_docs_ci_local.sh +``` + +To run against the exact checked-in commit (matching `actions/checkout` source +semantics), provide a git ref explicitly: + +```shell +./tools/run_cd_docs_ci_local.sh HEAD +``` + +This runs the same core steps as the GitHub docs workflow (configure, build, +and AutoAPI verification) and prints the same diagnostics used in CI. +Both GitHub Actions and the local Docker runner call the shared script +`tools/cd_docs_build_and_verify.sh`, so docs build/check logic is maintained +in one place. +System package parity is also centralized in +`.github/docker/cd_docs_apt_packages.txt`, which is used by both the workflow +and the local Docker image. + Installation ------------ diff --git a/tools/cd_docs_build_and_verify.sh b/tools/cd_docs_build_and_verify.sh new file mode 100755 index 000000000..05f9b489f --- /dev/null +++ b/tools/cd_docs_build_and_verify.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Single source of truth for docs CI build + AutoAPI verification. +BUILD_DIR="${BUILD_DIR:-tmp}" + +echo "== Configure (cmake) ==" +rm -rf "${BUILD_DIR}" +mkdir -p "${BUILD_DIR}" +# Provide a writable home for cmake/python cache when running as an arbitrary UID +mkdir -p "${BUILD_DIR}/.home" +cd "${BUILD_DIR}" +Python3_ROOT="$(python3 -c 'import sys; print(sys.exec_prefix)')" \ + CFLAGS='-Wno-missing-field-initializers' \ + cmake .. -DFORCE_EXAMPLES=ON -DWITH_DOC=ON + +echo "== Build (make) ==" +make VERBOSE=1 2>&1 | tee make-docs.log + +cd .. + +echo "== Verify Python AutoAPI output ==" +mapfile -t DOCS_CANDIDATES < <(find . -type f -path '*/doc/html/index.html' -not -path './gh-pages/*' | sed 's#/index.html$##' | sort -u) +if [[ "${#DOCS_CANDIDATES[@]}" -eq 0 ]]; then + echo "ERROR: Could not locate Sphinx HTML output directory (*/doc/html)." + echo "---- Candidate doc directories ----" + find . -maxdepth 4 -type d -name doc | sort || true + echo "---- Candidate html directories ----" + find . -maxdepth 6 -type d -name html | sort || true + exit 1 +fi + +DOCS_HTML_DIR="" +AUTOAPI_DIR="" +BEST_AUTOAPI_INDEX_COUNT=-1 +for CANDIDATE_DIR in "${DOCS_CANDIDATES[@]}"; do + CANDIDATE_AUTOAPI_DIR="${CANDIDATE_DIR}/autoapi" + CANDIDATE_AUTOAPI_INDEX_COUNT=$(find "${CANDIDATE_AUTOAPI_DIR}" -type f -name index.html 2>/dev/null | wc -l) + echo "Candidate docs HTML directory: ${CANDIDATE_DIR} (AutoAPI index pages: ${CANDIDATE_AUTOAPI_INDEX_COUNT})" + if [[ "${CANDIDATE_AUTOAPI_INDEX_COUNT}" -gt "${BEST_AUTOAPI_INDEX_COUNT}" ]]; then + BEST_AUTOAPI_INDEX_COUNT="${CANDIDATE_AUTOAPI_INDEX_COUNT}" + DOCS_HTML_DIR="${CANDIDATE_DIR}" + AUTOAPI_DIR="${CANDIDATE_AUTOAPI_DIR}" + fi +done + +echo "Using docs HTML directory: ${DOCS_HTML_DIR}" +if [[ -n "${GITHUB_ENV:-}" ]]; then + echo "DOCS_HTML_DIR=${DOCS_HTML_DIR}" >> "${GITHUB_ENV}" +fi + +if [[ "${BEST_AUTOAPI_INDEX_COUNT}" -lt 1 ]]; then + echo "ERROR: AutoAPI output was not found in any docs HTML directory." + echo "---- Make/Sphinx log tail ----" + if [[ -f "${BUILD_DIR}/make-docs.log" ]]; then + tail -n 300 "${BUILD_DIR}/make-docs.log" || true + else + echo "No ${BUILD_DIR}/make-docs.log found." + fi + echo "---- All AutoAPI directories under workspace ----" + find . -type d -path '*/autoapi' | sort || true + echo "---- All AutoAPI index files under workspace ----" + find . -type f -path '*/autoapi/*/index.html' | sort | head -n 200 || true + echo "---- Sphinx config excerpt ----" + find . -type f -path '*/doc/_build/conf.py' -print -exec sed -n '1,260p' {} \; | grep -E 'autoapi_(dirs|ignore|file_patterns|python_use_implicit_namespaces)' || true + echo "---- docs HTML listing ----" + ls -la "${DOCS_HTML_DIR}" || true + exit 1 +fi + +echo "Using AutoAPI directory: ${AUTOAPI_DIR}" +AUTOAPI_INDEX_COUNT="${BEST_AUTOAPI_INDEX_COUNT}" +echo "Detected ${AUTOAPI_INDEX_COUNT} AutoAPI index page(s)." + +if [[ ! -f "${DOCS_HTML_DIR}/api.html" ]] || ! grep -Eq 'autoapi/.*/index.html' "${DOCS_HTML_DIR}/api.html"; then + echo "ERROR: API landing page does not link to Python AutoAPI pages." + echo "---- api.html excerpt ----" + sed -n '460,620p' "${DOCS_HTML_DIR}/api.html" || true + exit 1 +fi + +find "${AUTOAPI_DIR}" -maxdepth 3 -type f -name index.html | sort | head -n 20 + +echo "== SUCCESS: docs build + AutoAPI verification completed ==" diff --git a/tools/run_cd_docs_ci_local.sh b/tools/run_cd_docs_ci_local.sh new file mode 100755 index 000000000..0129cd9a4 --- /dev/null +++ b/tools/run_cd_docs_ci_local.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +set -euo pipefail + +IMAGE_NAME="dlite-cd-docs-local" +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SOURCE_REF="${1:-}" +WORKTREE_PATH="" +SNAPSHOT_PATH="" +EXPECTED_SHA="" +MOUNT_PATH="" +SOURCE_LABEL="" + +cleanup() { + local _exit=$? + if [[ -n "${WORKTREE_PATH}" ]] && git -C "${REPO_ROOT}" worktree list --porcelain | grep -Fq "worktree ${WORKTREE_PATH}"; then + git -C "${REPO_ROOT}" worktree remove --force "${WORKTREE_PATH}" >/dev/null 2>&1 || true + fi + if [[ -n "${SNAPSHOT_PATH}" ]] && [[ -d "${SNAPSHOT_PATH}" ]]; then + # Docker may have created root-owned build artifacts inside the snapshot; + # use a throw-away container to remove them before falling back to rm -rf. + docker run --rm \ + -v "${SNAPSHOT_PATH}:/toclean" \ + ubuntu:24.04 \ + rm -rf /toclean/tmp /toclean/build 2>/dev/null || true + rm -rf "${SNAPSHOT_PATH}" 2>/dev/null || true + fi + exit "${_exit}" +} +trap cleanup EXIT + +if [[ -n "${SOURCE_REF}" ]]; then + EXPECTED_SHA="$(git -C "${REPO_ROOT}" rev-parse "${SOURCE_REF}")" + WORKTREE_PATH="$(mktemp -d "${REPO_ROOT}/.cd-docs-local-worktree.XXXXXX")" + rm -rf "${WORKTREE_PATH}" + git -C "${REPO_ROOT}" worktree add --detach "${WORKTREE_PATH}" "${SOURCE_REF}" >/dev/null + MOUNT_PATH="${WORKTREE_PATH}" + SOURCE_LABEL="git ref ${SOURCE_REF} (${EXPECTED_SHA})" +else + SNAPSHOT_PATH="$(mktemp -d "${REPO_ROOT}/.cd-docs-local-snapshot.XXXXXX")" + rsync -a \ + --exclude='.git/' \ + --exclude='tmp/' \ + --exclude='build*/' \ + --exclude='ci-build/' \ + --exclude='dlite-builddliteenv/' \ + "${REPO_ROOT}/" "${SNAPSHOT_PATH}/" + MOUNT_PATH="${SNAPSHOT_PATH}" + SOURCE_LABEL="working-tree snapshot (tracked + untracked files)" +fi + +echo "== Building local docs CI image (${IMAGE_NAME}) ==" +docker build \ + -f "${REPO_ROOT}/.github/docker/Dockerfile-cd-docs-local" \ + -t "${IMAGE_NAME}" \ + "${REPO_ROOT}" + +echo "== Running local docs CI repro from ${SOURCE_LABEL} ==" +# --user ensures build artifacts are created with the host UID so cleanup +# (rm -rf) never hits "Permission denied" on root-owned Docker output. +# HOME=/workspace/tmp/.home gives cmake/python a writable home cache dir. +if [[ -n "${SOURCE_REF}" ]]; then + docker run --rm -it \ + --user "$(id -u):$(id -g)" \ + -e HOME=/workspace/tmp/.home \ + -e "CI_SOURCE_REF=${SOURCE_REF}" \ + -e "CI_SOURCE_SHA=${EXPECTED_SHA}" \ + -v "${MOUNT_PATH}:/workspace" \ + -w /workspace \ + "${IMAGE_NAME}" +else + docker run --rm -it \ + --user "$(id -u):$(id -g)" \ + -e HOME=/workspace/tmp/.home \ + -e "CI_SOURCE_REF=WORKTREE" \ + -v "${MOUNT_PATH}:/workspace" \ + -w /workspace \ + "${IMAGE_NAME}" +fi