diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 21046d469..df838120b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -52,17 +52,19 @@ scope can be agreed upon. `just check` is the fast, non-mutating source and tooling gate, including the repository-owned Semgrep policy and its fixtures. `just ci` adds the - supported build and complete 128-entry CTest suite: 104 doctest unit - scenarios, 23 CLI integration tests, and one arithmetic-backend correctness - test. `just build-parallel` builds the distinct CGAL/oneTBB configuration and - runs its 128-entry suite: 103 ordinary doctest scenarios, one replayable - parallel stress launcher containing five scenarios, the same 23 CLI - integration tests, and the arithmetic correctness test. When changing C++ + supported build and complete 131-entry CTest suite: 106 doctest scenarios, + 23 CLI integration tests, one compiled C++ API example, and one + arithmetic-backend correctness test. `just build-parallel` builds the + distinct CGAL/oneTBB configuration and runs its 132-entry suite: 106 ordinary + doctest scenarios, one replayable parallel stress launcher containing five + scenarios, the same 23 CLI integration tests, the C++ API example, and the + arithmetic correctness test. When changing C++ behavior, also run `just clang-tidy` with the pinned LLVM 22 toolchain and review its advisory diagnostics. GitHub Actions runs `just ci` in its Ubuntu GCC, Ubuntu Clang, macOS AppleClang, and Windows MSVC jobs. The two - Ubuntu jobs also run `just build-parallel` to exercise the opt-in CGAL/oneTBB contract. Sanitizer and coverage - builds keep Release assertion semantics while adding their own debug information and optimization settings. A + Ubuntu jobs also run `just build-parallel` to exercise the opt-in CGAL/oneTBB contract, and the macOS job runs + `just viewer-build`. Pull requests run the separate coverage and generated-documentation gates. Sanitizer and + coverage builds keep Release assertion semantics while adding their own debug information and optimization settings. A separate full-suite Debug job is intentionally omitted because several fixtures traverse invalid intermediate triangulations and abort on CDT++ invariant assertions. Use `just build-debug` to compile production targets with CDT++ assertions enabled and run the 21 compatible CLI integration CTests. That preset defines `CGAL_NDEBUG` for diff --git a/.github/actions/setup-just/action.yml b/.github/actions/setup-just/action.yml index 104d56a28..158a326d1 100644 --- a/.github/actions/setup-just/action.yml +++ b/.github/actions/setup-just/action.yml @@ -36,6 +36,6 @@ runs: echo "version=$version" >> "$GITHUB_OUTPUT" - name: Install Just - uses: taiki-e/install-action@6a1bd70eaac3c8bdf093356838d7ee09fda951cf # v2.85.5 + uses: taiki-e/install-action@1beb33eee6d086258184383af9a538940be190ed # v2.85.6 with: tool: just@${{ steps.resolve.outputs.version }} diff --git a/.github/workflows/_sanitizer.yml b/.github/workflows/_sanitizer.yml index 0cb78b274..b3e250717 100644 --- a/.github/workflows/_sanitizer.yml +++ b/.github/workflows/_sanitizer.yml @@ -72,13 +72,19 @@ jobs: - name: Set up Just uses: ./.github/actions/setup-just + - name: Resolve sanitizer tool versions + id: tool-versions + run: | + llvm="$(just --evaluate llvm_version)" + printf 'llvm=%s\n' "$llvm" >> "$GITHUB_OUTPUT" + - name: Set up pkgx uses: pkgxdev/setup@4d4ae97af87ccb39ab8be4e073dea697fef2c6f7 # v5.0.0 - name: Set up cached vcpkg uses: ./.github/actions/setup-vcpkg with: - cache-key: clang-22 + cache-key: clang-${{ steps.tool-versions.outputs.llvm }} - name: Build and exercise sanitizer configuration env: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b4c5b6d3..29f07aa73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,40 +30,36 @@ jobs: include: - name: Ubuntu GCC os: ubuntu-latest - compiler_package: gnu.org/gcc@16 + compiler_family: gcc cc: gcc cxx: g++ compiler_cache: ccache - compiler_cache_key: gcc-16 run_parallel_contract: true - vcpkg_cache_key: gcc-16 + run_viewer_contract: false - name: Ubuntu Clang os: ubuntu-latest - compiler_package: llvm.org@22 + compiler_family: clang cc: clang cxx: clang++ compiler_cache: ccache - compiler_cache_key: clang-22 run_parallel_contract: true - vcpkg_cache_key: clang-22 + run_viewer_contract: false - name: macOS AppleClang os: macos-latest - compiler_package: "" + compiler_family: appleclang cc: clang cxx: clang++ compiler_cache: "" - compiler_cache_key: none run_parallel_contract: false - vcpkg_cache_key: appleclang + run_viewer_contract: true - name: Windows MSVC os: windows-latest - compiler_package: "" + compiler_family: msvc cc: cl cxx: cl compiler_cache: "" - compiler_cache_key: none run_parallel_contract: false - vcpkg_cache_key: msvc + run_viewer_contract: false steps: - name: Disable Git autocrlf on Windows @@ -96,13 +92,52 @@ jobs: { echo "cmake=$(just --evaluate cmake_version)" echo "ccache=$(just --evaluate ccache_version)" + echo "gcc=$(just --evaluate gcc_version)" echo "llvm=$(just --evaluate llvm_version)" echo "ninja=$(just --evaluate ninja_version)" echo "ninja-windows-wheel=$(just --evaluate ninja_windows_wheel_version)" + echo "python=$(just --evaluate python_version)" echo "typos=$(just --evaluate typos_version)" echo "uv=$(just --evaluate uv_version)" } >> "$GITHUB_OUTPUT" + - name: Resolve matrix compiler pin + id: compiler + shell: bash + env: + COMPILER_FAMILY: ${{ matrix.compiler_family }} + GCC_VERSION: ${{ steps.tool-versions.outputs.gcc }} + LLVM_VERSION: ${{ steps.tool-versions.outputs.llvm }} + run: | + case "$COMPILER_FAMILY" in + gcc) + version="$GCC_VERSION" + package="gnu.org/gcc@${version}" + cache_key="gcc-${version}" + ;; + clang) + version="$LLVM_VERSION" + package="llvm.org@${version}" + cache_key="clang-${version}" + ;; + appleclang) + package="" + cache_key="appleclang" + ;; + msvc) + package="" + cache_key="msvc" + ;; + *) + echo "Unsupported compiler family: ${COMPILER_FAMILY}" >&2 + exit 1 + ;; + esac + { + echo "package=${package}" + echo "cache_key=${cache_key}" + } >> "$GITHUB_OUTPUT" + - name: Set up typos uses: taiki-e/install-action@1beb33eee6d086258184383af9a538940be190ed # v2.85.6 with: @@ -112,8 +147,9 @@ jobs: if: runner.os != 'Windows' uses: pkgxdev/setup@4d4ae97af87ccb39ab8be4e073dea697fef2c6f7 # v5.0.0 with: + # Compiler packages are scoped to scripts/pkgx-build.sh so LLVM's + # ranlib does not shadow Apple's ranlib in the macOS viewer build. +: >- - llvm.org@${{ steps.tool-versions.outputs.llvm }} cmake.org@${{ steps.tool-versions.outputs.cmake }} ninja-build.org@${{ steps.tool-versions.outputs.ninja }} @@ -134,7 +170,7 @@ jobs: - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version-file: .python-version + python-version: ${{ steps.tool-versions.outputs.python }} - name: Set up uv uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 @@ -172,16 +208,16 @@ jobs: - name: Set up cached vcpkg uses: ./.github/actions/setup-vcpkg with: - cache-key: ${{ matrix.vcpkg_cache_key }} + cache-key: ${{ steps.compiler.outputs.cache_key }} - name: Restore compiler cache if: matrix.compiler_cache != '' uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: .cache/ccache - key: ccache-v1-${{ runner.os }}-${{ matrix.compiler_cache_key }}-${{ github.sha }} + key: ccache-v1-${{ runner.os }}-${{ steps.compiler.outputs.cache_key }}-${{ github.sha }} restore-keys: | - ccache-v1-${{ runner.os }}-${{ matrix.compiler_cache_key }}- + ccache-v1-${{ runner.os }}-${{ steps.compiler.outputs.cache_key }}- - name: Run the canonical local CI contract env: @@ -191,7 +227,7 @@ jobs: CCACHE_MAXSIZE: 1G CC: ${{ matrix.cc }} CDT_COMPILER_CACHE: ${{ matrix.compiler_cache }} - CDT_PKGX_COMPILER_PACKAGE: ${{ matrix.compiler_package }} + CDT_PKGX_COMPILER_PACKAGE: ${{ steps.compiler.outputs.package }} CXX: ${{ matrix.cxx }} run: just ci @@ -204,10 +240,14 @@ jobs: CCACHE_MAXSIZE: 1G CC: ${{ matrix.cc }} CDT_COMPILER_CACHE: ${{ matrix.compiler_cache }} - CDT_PKGX_COMPILER_PACKAGE: ${{ matrix.compiler_package }} + CDT_PKGX_COMPILER_PACKAGE: ${{ steps.compiler.outputs.package }} CXX: ${{ matrix.cxx }} run: just build-parallel + - name: Run the opt-in macOS viewer contract + if: matrix.run_viewer_contract + run: just viewer-build + - name: Report compiler cache statistics if: ${{ always() && matrix.compiler_cache != '' }} env: diff --git a/.github/workflows/codecov-upload.yml b/.github/workflows/codecov-upload.yml index 9946f85e1..b91263476 100644 --- a/.github/workflows/codecov-upload.yml +++ b/.github/workflows/codecov-upload.yml @@ -5,6 +5,9 @@ name: CodeCov push: branches: - main + pull_request: + branches: + - main workflow_dispatch: permissions: @@ -30,6 +33,24 @@ jobs: fetch-depth: 0 persist-credentials: false + - name: Set up Just + uses: ./.github/actions/setup-just + + - name: Resolve coverage tool versions + id: tool-versions + run: | + cmake="$(just --evaluate cmake_version)" + gcc="$(just --evaluate gcc_version)" + lcov="$(just --evaluate lcov_version)" + lcov_sha256="$(just --evaluate lcov_sha256)" + ninja="$(just --evaluate ninja_version)" + printf '%s\n' \ + "cmake=${cmake}" \ + "gcc=${gcc}" \ + "lcov=${lcov}" \ + "lcov_sha256=${lcov_sha256}" \ + "ninja=${ninja}" >> "$GITHUB_OUTPUT" + - name: Setup run: | sudo apt update @@ -38,10 +59,10 @@ jobs: curl lcov libtool-bin ninja-build python3-sphinx \ python3-sphinx-rtd-theme texinfo yasm - - name: Install LCOV 2.5 + - name: Install pinned LCOV env: - LCOV_SHA256: 7e5e5a154bd5f3557659c328cab376764e7abd238bb403c424472c296b175126 - LCOV_VERSION: "2.5" + LCOV_SHA256: ${{ steps.tool-versions.outputs.lcov_sha256 }} + LCOV_VERSION: ${{ steps.tool-versions.outputs.lcov }} run: | archive="${RUNNER_TEMP}/lcov-${LCOV_VERSION}.tar.gz" source_dir="${RUNNER_TEMP}/lcov-${LCOV_VERSION}" @@ -53,22 +74,11 @@ jobs: sudo make --directory "${source_dir}" install lcov --version - - name: Set up Just - uses: ./.github/actions/setup-just - - - name: Resolve coverage tool versions - id: tool-versions - run: | - { - echo "cmake=$(just --evaluate cmake_version)" - echo "ninja=$(just --evaluate ninja_version)" - } >> "$GITHUB_OUTPUT" - - name: Set up coverage toolchain uses: pkgxdev/setup@4d4ae97af87ccb39ab8be4e073dea697fef2c6f7 # v5.0.0 with: +: >- - gnu.org/gcc@16 + gnu.org/gcc@${{ steps.tool-versions.outputs.gcc }} cmake.org@${{ steps.tool-versions.outputs.cmake }} ninja-build.org@${{ steps.tool-versions.outputs.ninja }} @@ -94,7 +104,7 @@ jobs: - name: Set up cached vcpkg uses: ./.github/actions/setup-vcpkg with: - cache-key: gcc-16 + cache-key: gcc-${{ steps.tool-versions.outputs.gcc }} - name: Generate coverage reports env: @@ -103,11 +113,23 @@ jobs: GCOV: gcov run: just coverage + - name: Validate coverage reports + run: | + if [[ ! -s build/coverage.info ]]; then + echo "build/coverage.info is missing or empty." >&2 + exit 1 + fi + if [[ ! -d build/coverage-html ]] || + [[ -z "$(find build/coverage-html -type f -size +0c -print -quit)" ]]; then + echo "build/coverage-html is missing or empty." >&2 + exit 1 + fi + - name: Preserve coverage reports if: ${{ always() }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - if-no-files-found: warn + if-no-files-found: error name: coverage-report-${{ github.sha }} path: | build/coverage.info @@ -134,5 +156,6 @@ jobs: fail_ci_if_error: true files: build/coverage.info plugins: noop - use_oidc: true + token: ${{ secrets.CODECOV_TOKEN }} + use_oidc: ${{ github.actor != 'dependabot[bot]' }} verbose: true diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ddd65a934..bafa5a096 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -70,6 +70,13 @@ jobs: if: matrix.language == 'c-cpp' uses: ./.github/actions/setup-just + - name: Resolve C++ tool versions + if: matrix.language == 'c-cpp' + id: tool-versions + run: | + gcc="$(just --evaluate gcc_version)" + printf 'gcc=%s\n' "$gcc" >> "$GITHUB_OUTPUT" + - name: Set up C++ environment with pkgx if: matrix.language == 'c-cpp' uses: pkgxdev/setup@4d4ae97af87ccb39ab8be4e073dea697fef2c6f7 # v5.0.0 @@ -78,13 +85,13 @@ jobs: if: matrix.language == 'c-cpp' uses: ./.github/actions/setup-vcpkg with: - cache-key: gcc-16 + cache-key: gcc-${{ steps.tool-versions.outputs.gcc }} - name: Prepare C++ dependencies outside CodeQL tracing if: matrix.language == 'c-cpp' env: CC: gcc - CDT_PKGX_COMPILER_PACKAGE: gnu.org/gcc@16 + CDT_PKGX_COMPILER_PACKAGE: gnu.org/gcc@${{ steps.tool-versions.outputs.gcc }} CXX: g++ run: just codeql-prepare @@ -98,7 +105,7 @@ jobs: if: matrix.language == 'c-cpp' env: CC: gcc - CDT_PKGX_COMPILER_PACKAGE: gnu.org/gcc@16 + CDT_PKGX_COMPILER_PACKAGE: gnu.org/gcc@${{ steps.tool-versions.outputs.gcc }} CXX: g++ run: just codeql-build diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 1184eb367..0782e1796 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -5,6 +5,9 @@ name: Doxygen push: branches: - main + pull_request: + branches: + - main workflow_dispatch: permissions: @@ -15,7 +18,28 @@ concurrency: cancel-in-progress: true jobs: + validate: + name: docs + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up Just + uses: ./.github/actions/setup-just + + - name: Set up pkgx + uses: pkgxdev/setup@4d4ae97af87ccb39ab8be4e073dea697fef2c6f7 # v5.0.0 + + - name: Validate documentation + run: just docs-check + deploy: + if: github.event_name != 'pull_request' + needs: + - validate runs-on: ubuntu-latest permissions: contents: write @@ -31,7 +55,7 @@ jobs: - name: Set up pkgx uses: pkgxdev/setup@4d4ae97af87ccb39ab8be4e073dea697fef2c6f7 # v5.0.0 - - name: Build documentation + - name: Build documentation for publication run: just docs - name: Publish documentation diff --git a/.pinact.yaml b/.pinact.yaml index a765c9872..3835293ef 100644 --- a/.pinact.yaml +++ b/.pinact.yaml @@ -1,6 +1,8 @@ --- version: 3 files: + - pattern: .github/actions/*/action.yml + - pattern: .github/actions/*/action.yaml - pattern: .github/workflows/*.yml - pattern: .github/workflows/*.yaml separator: " # " diff --git a/.python-version b/.python-version index 6324d401a..3f0a10fda 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.14 +3.14.6 diff --git a/Justfile b/Justfile index 33b3852fc..ed5103089 100644 --- a/Justfile +++ b/Justfile @@ -11,9 +11,12 @@ cgal_benchmark_binary := if os_family() == "windows" { "out/build/reference/test cmake_minimum_version := "4.4.0" cmake_version := "4.4.1" doxygen_version := "1.16.1" +gcc_version := "16" git_cliff_version := "2.13.1" graphviz_version := "15.1.0" just_version := "1.58.0" +lcov_sha256 := "7e5e5a154bd5f3557659c328cab376764e7abd238bb403c424472c296b175126" +lcov_version := "2.5" llvm_version := "22" ninja_version := "1.13.2" ninja_windows_wheel_version := "1.13.0" @@ -21,7 +24,7 @@ parallel_cgal_benchmark_binary := if os_family() == "windows" { "out/build/paral pinact_module := "github.com/suzuki-shunsuke/pinact/v4/cmd/pinact@v" + pinact_version pinact_version := "4.1.1" primary_binary := if os_family() == "windows" { "out/build/reference/src/cdt.exe" } else { "out/build/reference/src/cdt" } -python_version := "3.14.6" +python_version := trim(read(".python-version")) reference_fixture_binary := if os_family() == "windows" { "out/build/reference/tests/CDT_reference_fixture.exe" } else { "out/build/reference/tests/CDT_reference_fixture" } rng_benchmark_binary := if os_family() == "windows" { "out/build/reference/tests/CDT_rng_benchmark.exe" } else { "out/build/reference/tests/CDT_rng_benchmark" } typos_version := "1.49.0" diff --git a/README.md b/README.md index 471e95dc7..2fcfda345 100644 --- a/README.md +++ b/README.md @@ -252,7 +252,7 @@ API example, and one arithmetic-backend correctness test. The same `reference-smoke` preset is the supported local and CI contract; there are no overlapping focused registrations that can pass while omitting another doctest suite. The current -`parallel` preset also registers 129 tests: 103 ordinary doctest scenarios, one +`parallel` preset also registers 132 tests: 106 ordinary doctest scenarios, one parallel launcher containing five scenarios, the same 23 CLI integration tests, the C++ API example, and the arithmetic correctness test. The parallel-enabled @@ -260,9 +260,9 @@ AddressSanitizer configuration exercises the same replayable stress contract. ## Setup -This project uses [CMake]+[Ninja] to build C++23 sources and [vcpkg] manifest mode to manage C++ libraries. macOS with -AppleClang is the primary v1.0.0-rc3 validation target; the remaining compiler and platform matrix will be recorded as -it is verified. +This project uses [CMake]+[Ninja] to build C++23 sources and [vcpkg] manifest mode to manage C++ libraries. The +v1.0.0-rc3 CI matrix exercises Ubuntu with GCC 16 and Clang 22, macOS with AppleClang, and Windows with MSVC. CMake +also rejects compiler versions below the supported C++23 floor: GCC 13.3, Clang 22, AppleClang 15, and MSVC 19.34. ### Prerequisites @@ -330,8 +330,10 @@ citation fields, YAML, GitHub Actions syntax and security, whitespace, and CMake check and the supported build/test contract. Documentation validation remains available separately through `just docs-check`. The GitHub Actions Ubuntu GCC, Ubuntu Clang, macOS AppleClang, and Windows MSVC jobs all run `just ci`; the two Ubuntu jobs also -run `just build-parallel`. The Ubuntu compiler jobs use the pinned pkgx ccache -package with a compiler-specific persistent cache. Sanitizer builds use Release +run `just build-parallel`, while the macOS job builds and smoke-tests the opt-in +viewer. Pull requests also run the distinct coverage and generated-documentation +gates. The Ubuntu compiler jobs use the pinned pkgx ccache package with a +compiler-specific persistent cache. Sanitizer builds use Release semantics with sanitizer-provided `-O1 -g` flags, while coverage uses Release semantics with coverage-provided `-O0 -g` flags; no duplicate full-suite Debug job is needed. Windows continues to compile with native MSVC; the @@ -410,7 +412,8 @@ invocations must expose CMake 4.4.0 or newer on `PATH`; the canonical pkgx-backe The archival Qt viewer is deliberately outside that cross-platform headless contract. On macOS, `just viewer-build` selects the separate `viewer` preset and vcpkg feature, builds `cdt-viewer` under `out/build/viewer`, and runs its -noninteractive image smoke test. Qt and Eigen are installed only in that build tree. See +noninteractive image smoke test. The macOS CI cell runs that same focused recipe after the headless contract; Qt and +Eigen are installed only in the viewer build tree. See [`docs/viewer.md`](docs/viewer.md) for regeneration, version pins, and portability rules. ### Project Layout @@ -537,8 +540,9 @@ only after strict generation and generated-site validation succeed. Both recipes Mermaid CDN dependency. `scripts/validate_generated_site.py` preserves that bounded workaround by checking the actual HTML, local links and fragments, duplicate IDs and link labels, and required assets. `USE_MATHJAX` allows [MathJax] to render LaTeX formulae, and `HAVE_DOT` enables [GraphViz] diagrams. Documentation validation is intentionally separate from the cross-platform -`just ci` contract. The documentation workflow runs `just docs` on Ubuntu and publishes its output to the `gh-pages` -branch. +`just ci` contract. The documentation workflow runs `just docs-check` as the stable `docs` pull-request gate. After a +successful `main` validation, a separate least-privilege job runs `just docs` and publishes its output to the +`gh-pages` branch. ## Citing CDT++ @@ -557,9 +561,9 @@ doctest scenarios, 23 executable integration tests covering normal CLI use and invalid-boundary rejection, one compiled C++ API example, and one arithmetic-backend correctness test labeled `scientific`. The parallel-enabled AddressSanitizer and `parallel` -configurations register 103 ordinary doctest scenarios, one launcher +configurations register 106 ordinary doctest scenarios, one launcher containing five scenarios labeled `unit`, `parallel`, and `configuration`, the -same 23 integration tests, the C++ API example, and the arithmetic test, for 129 +same 23 integration tests, the C++ API example, and the arithmetic test, for 132 CTest entries. Every process-level test is labeled `integration`, and invalid-input tests also carry the `cli-boundary` subcategory. Run `just ci` for the complete local @@ -620,8 +624,8 @@ assignments in `Utilities.hpp`. The coverage recipe keeps LCOV's warnings visible and requires that exact count during extraction and report generation; new or removed inconsistencies therefore fail the command for review. -The Codecov workflow runs this recipe, uploads only `build/coverage.info` with -OIDC, and preserves both reports as a 14-day GitHub Actions artifact for local +The Codecov workflow runs this recipe for pull requests and `main`, uploads only `build/coverage.info` with OIDC, and +preserves both reports as a 14-day GitHub Actions artifact for local diagnosis. If report generation fails, the workflow also preserves the gcov inputs and CTest diagnostics for seven days. It does not rely on Codecov's automatic gcov discovery. Codecov retains the LCOV branch detail but counts an diff --git a/docs/RELEASING.md b/docs/RELEASING.md index d5f10ea81..1f13e09bb 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -116,6 +116,20 @@ just ci `just release-check` verifies synchronization across CMake, vcpkg, Python, `uv.lock`, Doxygen, citation metadata, and active release-candidate references. +The protected-branch ruleset uses stable summary contexts for the deterministic +release gates: + +- `build` covers the Ubuntu GCC, Ubuntu Clang, macOS AppleClang, and Windows + MSVC `just ci` matrix, the two Ubuntu parallel contracts, and the macOS viewer + smoke contract. +- `build / AddressSanitizer + UndefinedBehaviorSanitizer` and + `build / LeakSanitizer` exercise the stable Linux sanitizer configurations. +- `CodeRabbit` records the required review approval. + +ThreadSanitizer and CodeQL remain advisory diagnostics rather than protected +branch requirements; MemorySanitizer remains manual and experimental. Do not +promote those contexts without a separate stability decision. + Review and commit only release artifacts: ```bash diff --git a/docs/viewer.md b/docs/viewer.md index ab80eae9e..a810aada5 100644 --- a/docs/viewer.md +++ b/docs/viewer.md @@ -12,6 +12,9 @@ On macOS, a clean checkout can build the viewer and run its noninteractive rende just viewer-build ``` +The macOS CI matrix cell runs this same focused recipe after the required headless build/test contract. Linux and +Windows CI remain headless and validate only the tracked viewer inputs and canonical artifact through `just check`. + Regenerate and validate the tracked README hero image with one command: ```console diff --git a/scripts/debug.sh b/scripts/debug.sh deleted file mode 100755 index b4eae3410..000000000 --- a/scripts/debug.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -# Before running this script, make sure $VCPKG_ROOT is set, e.g. -# VCPKG_ROOT="$HOME"/vcpkg && export VCPKG_ROOT - -set -euo pipefail - -script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" -repo_root="$(cd -- "${script_dir}/.." && pwd)" -build_dir="${repo_root}/build" - -cd -- "${repo_root}" -rm -rf -- "${build_dir}" -cmake --preset debug -cmake --build "${build_dir}" -ctest --test-dir "${build_dir}" --output-on-failure -j2 diff --git a/scripts/tests/test_justfile_discoverability.py b/scripts/tests/test_justfile_discoverability.py index ce61a925f..64fb61b36 100644 --- a/scripts/tests/test_justfile_discoverability.py +++ b/scripts/tests/test_justfile_discoverability.py @@ -9,6 +9,8 @@ from pathlib import Path from typing import Any, cast +import yaml + REPO_ROOT = Path(__file__).resolve().parents[2] WORKFLOW_VERSION_LOOKUP = re.compile(r"\bjust --evaluate ([a-z][a-z0-9_-]*)") UV_INVOCATION = re.compile(r"(? dict[str, dict[str, Any]]: return cast("dict[str, dict[str, Any]]", _just_document()["recipes"]) +def _workflow_document(filename: str) -> dict[str, Any]: + """Return one parsed GitHub Actions workflow.""" + path = REPO_ROOT / ".github" / "workflows" / filename + return cast("dict[str, Any]", yaml.safe_load(path.read_text(encoding="utf-8"))) + + def _dependency_names(recipe: dict[str, Any]) -> set[str]: """Return the direct recipe dependencies recorded by Just.""" return {dependency["recipe"] for dependency in recipe["dependencies"]} @@ -168,6 +176,38 @@ def test_workflow_tool_lookups_resolve_from_just(self) -> None: self.assertIn(name, assignment_names) self.assertTrue(_run_just("--evaluate", name).stdout.strip()) + def test_python_patch_version_has_one_source_of_truth(self) -> None: + """Local and workflow Python selection must resolve the same exact patch.""" + version_file = (REPO_ROOT / ".python-version").read_text(encoding="utf-8").strip() + + self.assertRegex(version_file, r"^[0-9]+[.][0-9]+[.][0-9]+$") + self.assertEqual(_run_just("--evaluate", "python_version").stdout.strip(), version_file) + + def test_deterministic_release_workflows_gate_pull_requests(self) -> None: + """Coverage and generated documentation must validate before merge.""" + expectations = ( + ("codecov-upload.yml", "codecov", "CodeCov"), + ("doxygen.yml", "validate", "docs"), + ) + for filename, job_id, stable_name in expectations: + with self.subTest(workflow=filename): + workflow = _workflow_document(filename) + self.assertEqual(workflow["on"]["pull_request"]["branches"], ["main"]) + self.assertEqual(workflow["jobs"][job_id]["name"], stable_name) + + def test_macos_matrix_runs_the_opt_in_viewer_contract(self) -> None: + """The archival viewer stays opt-in locally but is exercised on its supported CI host.""" + workflow = _workflow_document("ci.yml") + platform = workflow["jobs"]["platform"] + matrix = platform["strategy"]["matrix"]["include"] + viewer_cells = [cell["name"] for cell in matrix if cell["run_viewer_contract"]] + viewer_steps = [step for step in platform["steps"] if step.get("name") == "Run the opt-in macOS viewer contract"] + + self.assertEqual(viewer_cells, ["macOS AppleClang"]) + self.assertEqual(len(viewer_steps), 1) + self.assertEqual(viewer_steps[0]["if"], "matrix.run_viewer_contract") + self.assertEqual(viewer_steps[0]["run"], "just viewer-build") + if __name__ == "__main__": unittest.main()