# CI / CD Single source of truth for **how continuous integration is wired** across the two forges this project lives on. If you add, move or rename a workflow, update this page. ## TL;DR | Forge | Remote | Role | CI system | Config | Runner | |---|---|---|---|---|---| | **eulernest Gitea** | `origin` | Authoritative gate (full test + quality suite) | Gitea Actions | `.gitea/workflows/` | self-hosted Raspberry Pi (ARM64), label `eulernest` | | **Codeberg** | `codeberg` | Public mirror + public build badge | Woodpecker **and** Forgejo Actions | `.woodpecker.yml`, `.forgejo/workflows/` | Codeberg shared runners, label `docker` | The two forges are connected by a one-way mirror: every push to `main` on eulernest is force-mirrored to Codeberg (see [Mirror](#mirror-eulernest--codeberg) below). Codeberg never pushes back. Why two CI systems on Codeberg? They are independent and either may be enabled per-repo; providing both means the public build status is green regardless of which one the repo has switched on. They run the **same** `test-fast` build. --- ## eulernest Gitea (`origin`) — the real gate Self-hosted Gitea with a single self-hosted runner: a Raspberry Pi (ARM64, 3–4 GB RAM). Because the runner is RAM-constrained, the heavy jobs are **keyword-gated** (opt-in per commit) rather than run on every push. All C++ jobs use the private image `git.eulernest.eu/conformallab/ci-cpp:latest` (built from `.gitea/docker/Dockerfile.ci-cpp` — Ubuntu 22.04 + Node 20 + cmake + build-essential + libboost-dev + doxygen). ### `.gitea/workflows/cpp-tests.yml` | Job | Trigger | Memory cap | What it does | |---|---|---|---| | `test-fast` | every push to `main` / `claude/**` / `feature/**` / `review/**` + every PR | 800 MB | Pure-math suite (Clausen, ImLi₂, hyper-ideal). Serial build (`-j1`) to avoid OOM. Eigen vendored, GTest via FetchContent. | | `test-cgal` | commit message contains **`/test-cgal`** | 2000 MB | Full CGAL suite via `LOW_MEMORY_BUILD` (`-O0`, no PCH, unity batch 1, `--no-keep-memory`). Then `check-test-counts.sh` + `try_it.sh` smoke test. | | `quality-gates` | commit message contains **`/quality-gates`** | 600 MB | License headers, CGAL conventions, codespell, shellcheck, coverage (gate currently in report-only mode, `SKIP_COVERAGE_GATE=1`). | > One keyword per commit — the Pi cannot sustain multiple Docker containers at > once (`/ci-all` was removed for this reason). Example: > `git commit -m "fix: correct angle formula /test-cgal"`. ### Other eulernest workflows | File | Trigger | Purpose | Blocks merge? | |---|---|---|---| | `doc-build.yaml` | push to main branches + manual | Doxygen HTML + warning stats | No (`continue-on-error`) | | `markdown-links.yml` | push + weekly cron (Mon 05:00 UTC) + manual | Link-rot check across docs | Yes on push | | `doxygen-pages.yml` | manual only | Publish Doxygen HTML to Codeberg Pages (`tmoussa.codeberg.page/ConformalLabpp/`) | n/a | | `perf-compile-time.yml` | manual only | Compile-time benchmark | n/a | | `mirror-to-codeberg.yml` | push to `main` | Mirror all branches to Codeberg (see below) | n/a | --- ## Codeberg (`codeberg`) — public mirror CI Codeberg's shared runners **cannot** pull the private `ci-cpp` image and are not the `eulernest` Pi, so the Codeberg configs differ from Gitea in exactly two ways: 1. **Runner label** `docker` (Codeberg shared runners), not `eulernest`. 2. **Public base image**, with build tools `apt`-installed at run time: - Woodpecker: `debian:bookworm` - Forgejo Actions: `node:20-bookworm` (Node is needed by `actions/checkout@v4`; cmake/g++/make are installed in the first step). Both run **only** `test-fast` — the pure-math suite. No CGAL/Boost/Wayland, so they are fast (< 2 min) and headless. Eigen is vendored, GoogleTest is fetched at configure time (Codeberg runners have network). A failing build or any failing `ctest` turns the public CI badge red. | File | CI system | Enable via | |---|---|---| | `.woodpecker.yml` | Woodpecker | https://ci.codeberg.org → enable repo | | `.forgejo/workflows/cpp-tests.yml` | Forgejo Actions | repo *Settings → Actions* (shared runner label `docker`) | > These configs are committed in the repo and reach Codeberg through the mirror > — they are not maintained separately on Codeberg. --- ## Mirror (eulernest → Codeberg) `.gitea/workflows/mirror-to-codeberg.yml` runs on every push to `main` and does a `git push --mirror` from eulernest to `codeberg.org/TMoussa/ConformalLabpp.git`. It uses two secrets: - `MIRROR_TOKEN` — read access to the eulernest source repo. - `CODEBERG_TOKEN` — push access to the Codeberg mirror. The mirror is **one-way and authoritative-from-eulernest**: do not commit directly on Codeberg, it will be overwritten on the next mirror run. --- ## The one rule > **eulernest is the gate, Codeberg is the shop window.** > Merge decisions are made on eulernest CI (full suite). Codeberg CI exists so > the public repo also shows a green/red build status to outside readers. ## See also - [dependencies.md](dependencies.md) — what each build mode requires. - [project-structure.md](project-structure.md) — where the test targets live. - [../api/tests.md](../api/tests.md) — per-suite test counts CI checks against. - [../contributing.md](../contributing.md) — the git workflow that feeds CI.