ci+licenses: promote 4 trivial gates to required CI + THIRD-PARTY-LICENSES.md

Two reviewer-facing additions consolidated into one commit:

(1) New `quality-gates` job in .gitea/workflows/cpp-tests.yml
    ──────────────────────────────────────────────────────────
    Runs in parallel with test-cgal after test-fast.  Installs
    `codespell` + `shellcheck` (apt) into the existing ci-cpp container,
    then executes four scripts strictly (exit 1 on any finding):
      * license-headers.sh   — 66/66 files carry SPDX MIT
      * cgal-conventions.py  — 0 violations across 6 CGAL public headers
      * codespell.sh         — 0 typos across docs + source + scripts
      * shellcheck.sh        — 0 findings across 16 shell scripts

    Each ran at 0 findings locally before promotion.  Total wall-time
    on the eulernest runner: ~30 s.

(2) New code/deps/THIRD-PARTY-LICENSES.md
    ──────────────────────────────────────
    Enumerates every vendored dep under code/deps/, plus auto-fetched
    GoogleTest, plus system-required Boost, with:
      * upstream project + version + SPDX identifier
      * compatibility note for MIT distribution
      * downstream-packager license matrix (header-only consumer vs
        CLI binary) clarifying the LGPL §3 vs §4 distinction

    Required for any future Linux-distribution packaging and for the
    CGAL submission's compliance check.

    Also fixes a `code/.gitignore` gap: the `deps/*` wildcard was
    catching the new file; added `!deps/THIRD-PARTY-LICENSES.md` to
    the exclusion list so it's actually tracked.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Tarik Moussa
2026-05-24 21:40:40 +02:00
parent 8d34be76a7
commit fe1702ad35
3 changed files with 147 additions and 0 deletions

View File

@@ -112,3 +112,51 @@ jobs:
# the price of guaranteeing the documented workflow stays working. # the price of guaranteeing the documented workflow stays working.
- name: End-to-end smoke test (scripts/try_it.sh) - name: End-to-end smoke test (scripts/try_it.sh)
run: bash scripts/try_it.sh run: bash scripts/try_it.sh
# ─────────────────────────────────────────────────────────────────────────────
# Job 3 — quality-gates (style + convention block)
#
# Cheap, deterministic checks that should never break unless a contributor
# introduces a regression. Each gate is a script under scripts/quality/
# and exits 0 only when its tree is clean. These ran for weeks locally
# at zero findings before being promoted here.
#
# Tools installed at job-start (the ci-cpp image already has python3 +
# bash; we add codespell + shellcheck on top). Total wall-time: ~30 s
# on the eulernest runner.
#
# Strictly required for merges into main/dev — a regression fails the PR.
# ─────────────────────────────────────────────────────────────────────────────
quality-gates:
needs: test-fast
runs-on: eulernest
container:
image: git.eulernest.eu/conformallab/ci-cpp:latest
steps:
- uses: actions/checkout@v4
- name: Install codespell + shellcheck (job-local)
run: |
apt-get update -qq
apt-get install -y --no-install-recommends \
codespell shellcheck
- name: License headers (every C++ source carries MIT SPDX)
run: bash scripts/quality/license-headers.sh
- name: CGAL conventions (6 rules over CGAL public headers)
run: python3 scripts/quality/cgal-conventions.py
- name: codespell (docs + source comments + script messages)
run: bash scripts/quality/codespell.sh
- name: shellcheck (scripts/**/*.sh, severity=warning, strict)
run: bash scripts/quality/shellcheck.sh --strict
- name: Summary
if: always()
run: |
echo "QUALITY ▸ all four gates passed."
echo " see scripts/quality/README.md for the full catalogue"
echo " (sanitizers, clang-tidy, coverage, etc. are local-only)"

1
code/.gitignore vendored
View File

@@ -13,6 +13,7 @@ deps/*
!deps/tarballs !deps/tarballs
!deps/single_includes/ !deps/single_includes/
!deps/CMakeLists.txt !deps/CMakeLists.txt
!deps/THIRD-PARTY-LICENSES.md
# macOS iCloud Drive duplicates ("file 2.cpp", "file 2.hpp", …) # macOS iCloud Drive duplicates ("file 2.cpp", "file 2.hpp", …)
* 2.* * 2.*

View File

@@ -0,0 +1,98 @@
# Third-party licenses
This directory contains source code from external projects that
conformallab++ vendors at fixed versions for build reproducibility.
Each project is governed by its own license; this file enumerates them
so downstream packagers, distributors, and reviewers can audit
compatibility without crawling each upstream tarball.
> **Why vendored at all?** conformallab++ is header-only and ships
> nothing it does not author except the optional CLI binary
> (`-DWITH_CGAL=ON`). Vendoring guarantees that the CGAL / Eigen /
> Boost API surface every contributor sees is identical, removing
> "works on my machine because I have CGAL 6.0 not 5.6" failure modes
> during early review. Downstream packagers replacing the vendored
> trees with system installs is supported and is the recommended path
> for distribution-level packaging (see `doc/architecture/dependencies.md`).
## conformallab++ itself
| Item | License | Notes |
|---|---|---|
| `code/include/**`, `code/src/**`, `code/tests/**`, `scripts/**`, `doc/**` | **MIT** (see `LICENSE` at repo root) | Every C++ source file carries `SPDX-License-Identifier: MIT`; CI gate `scripts/quality/license-headers.sh` enforces this. |
## Vendored dependencies
The table below lists each tree under `code/deps/`, its upstream
license, the SPDX identifier, and any compatibility note relevant to
shipping conformallab++ as MIT.
| Directory | Upstream project | Version | License (SPDX) | Compatibility with MIT distribution | Notes |
|---|---|---|---|---|---|
| `CGAL-6.1.1/` | [CGAL](https://www.cgal.org) | 6.1.1 | **LGPL-3.0-or-later** (most headers) + **GPL-3.0-or-later** (a small subset — see CGAL's per-header `\cgal_license{...}` macro) | Header-only consumption is compatible; we ONLY include LGPL'd parts (`Surface_mesh`, `Polygon_mesh_processing`, BGL adapters, kernels). | conformallab++ does not include any of the GPL-only CGAL packages (e.g. `Triangulation_3` parts, certain mesh-3 internals). The `\cgal_license` macro is checked at compile time and would fail the build if a GPL-only header were transitively pulled in. Commercial licenses are available from GeometryFactory for users who can't accept (L)GPL. |
| `eigen-3.4.0/` | [Eigen](https://eigen.tuxfamily.org) | 3.4.0 | **MPL-2.0** for almost everything, **LGPL-2.1-or-later** for a few legacy files (e.g. `Eigen/src/Core/util/NonMPL2.h` gates these) | MPL-2.0 is permissive enough for MIT; the LGPL files are NOT pulled in by `<Eigen/Dense>` / `<Eigen/Sparse>` (the only Eigen headers conformallab++ includes). | We define no preprocessor flag that activates the non-MPL2 code paths. The default Eigen build is pure MPL-2.0. |
| `libigl-2.6.0/` | [libigl](https://libigl.github.io) | 2.6.0 | **MPL-2.0** | Compatible with MIT distribution. | Only the viewer subsystem under `code/src/viewer/` uses libigl, and only when `-DWITH_VIEWER=ON`. The library headers and the CGAL wrapper headers do not depend on libigl. |
| `libigl-glad/` | [Glad](https://glad.dav1d.de/) (the generated OpenGL loader libigl ships) | bundled with libigl 2.6.0 | **MIT** (the generator's output is licensed permissively; the loader code itself is in the public domain via the original Khronos headers) | Compatible. | Built only with `-DWITH_VIEWER=ON`. |
| `glfw-3.4/` | [GLFW](https://www.glfw.org) | 3.4 | **zlib/libpng** | Permissive; compatible with MIT. | Built only with `-DWITH_VIEWER=ON`. See `code/deps/glfw-3.4/LICENSE.md` for the verbatim text. |
| `single_includes/json.hpp` | [nlohmann/json](https://github.com/nlohmann/json) | 3.x (header-only single-include) | **MIT** | Identical to ours. | The file itself carries the SPDX header `MIT`; see `code/deps/single_includes/json.hpp` first lines. |
| `tarballs/` | (build-artefact cache) | — | n/a | n/a | This directory just caches the downloaded source tarballs to avoid re-downloading on every clean build. The tarballs are bit-for-bit identical to the upstream releases. |
## Auto-fetched (not vendored)
These are pulled by CMake `FetchContent` at configure time. They are
**not** redistributed by conformallab++; the user's CMake fetches them
during build. We list them anyway for transparency.
| Item | Upstream | Version | License | Fetched by |
|---|---|---|---|---|
| **GoogleTest** | https://github.com/google/googletest | v1.14.0 | **BSD-3-Clause** | `code/CMakeLists.txt` (test target only) |
## System dependencies (required at build time, not redistributed)
| Item | Where it lives | License | Purpose |
|---|---|---|---|
| **Boost** (header-only subset) | system package (`apt install libboost-dev`, etc.) | **Boost Software License 1.0** | Required by CGAL's BGL adapters (only when `WITH_CGAL=ON` or `WITH_CGAL_TESTS=ON`). |
| **C++17 standard library** | the compiler's libstdc++ / libc++ / msvc | LGPL-3.0 with exception / Apache 2.0 with LLVM exception / MSVC redist | normal compiler runtime. |
## Summary for downstream packagers
If you are packaging conformallab++ for a distribution, the practical
license matrix is:
```
binary you ship (CLI app, -DWITH_CGAL=ON)
├── conformallab++ (MIT)
├── CGAL (LGPL-3.0-or-later — comply with §4 LGPL: source
│ of CGAL must be obtainable or shipped)
├── Eigen (MPL-2.0 — comply with §3 MPL: any modifications
│ must be released under MPL-2.0)
├── libigl (MPL-2.0 — same as Eigen)
├── GLFW (zlib/libpng — acknowledgement in product docs)
├── Glad (MIT — preserve copyright notice)
└── Boost (headers) (BSL-1.0 — preserve copyright notice)
header-only consumer (just #include our headers)
├── conformallab++ (MIT)
├── Eigen (MPL-2.0 transitively)
├── CGAL (LGPL-3.0-or-later transitively)
└── Boost (headers) (BSL-1.0 transitively, only if you include any
CGAL/* header)
```
The header-only consumer typically doesn't trigger LGPL §4 obligations
because LGPL §3 explicitly permits use of LGPL'd material as
"templates, inline functions, macros" by an "Application" without
imposing copyleft on the Application — which is exactly the
header-only consumption pattern.
If you have specific compliance questions, the upstream license texts
are authoritative; this file is a navigational aid.
## How this file is maintained
* Updated whenever a `code/deps/` tree is added, removed, or version-bumped.
* Cross-referenced by `doc/architecture/dependencies.md`.
* There is no CI gate that auto-verifies the SPDX entries against
upstream — that would require either an SBOM tool (e.g. `syft`,
`tern`) or a manual audit. The current policy is "review on
dep-tree change", logged in the commit message of the bump.