diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml new file mode 100644 index 0000000..17a5f1f --- /dev/null +++ b/.github/workflows/build-containers.yml @@ -0,0 +1,175 @@ +# Build + publish the wro.cpp toolset containers to ghcr.io/wrocpp/cpp-*. +# Matrix build; cluster images depend on cpp-base, so cpp-base goes +# first and the rest run after it succeeds. +# +# Triggers: +# * push to any branch touching containers/** -> build all + tag the branch +# * push to main touching containers/** -> tag rolling 2026-05 +# * workflow_dispatch -> build everything ad-hoc +# +# Tag scheme: +# * 2026-05 : rolling release, what /toolset/ pages link to +# * sha- : per-build snapshot, useful for debugging +# * branch- : per-feature-branch builds for PR review +# +# SECURITY: every value used in run: blocks comes from controlled +# system context (github.ref_name, github.sha, github.actor, secrets). +# No user-controlled input (issue body, PR title, commit message body) +# flows into shell. See +# https://github.blog/security/vulnerability-research/how-to-catch-github-actions-workflow-injections-before-attackers-do/ + +name: build-containers + +on: + push: + branches: ["**"] + paths: + - "containers/**" + - ".github/workflows/build-containers.yml" + workflow_dispatch: + +env: + REGISTRY: ghcr.io + ORG: wrocpp + +jobs: + base: + name: build cpp-base + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + outputs: + tag: ${{ steps.meta.outputs.tag }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Resolve tag + id: meta + env: + REF_NAME: ${{ github.ref_name }} + run: | + if [ "$REF_NAME" = "main" ]; then + echo "tag=2026-05" >> "$GITHUB_OUTPUT" + else + sanitized=$(echo "$REF_NAME" | tr '/' '-') + echo "tag=branch-${sanitized}" >> "$GITHUB_OUTPUT" + fi + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build + push cpp-base (multi-arch) + uses: docker/build-push-action@v5 + with: + context: containers/base + push: true + platforms: linux/amd64,linux/arm64 + tags: | + ${{ env.REGISTRY }}/${{ env.ORG }}/cpp-base:${{ steps.meta.outputs.tag }} + ${{ env.REGISTRY }}/${{ env.ORG }}/cpp-base:sha-${{ github.sha }} + cache-from: type=gha,scope=cpp-base + cache-to: type=gha,mode=max,scope=cpp-base + + cluster: + name: build cpp-${{ matrix.image }} + needs: base + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: + image: [safety, performance, security, compliance] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build + push cpp-${{ matrix.image }} + uses: docker/build-push-action@v5 + with: + context: containers/${{ matrix.image }} + push: true + platforms: linux/amd64,linux/arm64 + build-args: | + BASE_TAG=${{ needs.base.outputs.tag }} + tags: | + ${{ env.REGISTRY }}/${{ env.ORG }}/cpp-${{ matrix.image }}:${{ needs.base.outputs.tag }} + ${{ env.REGISTRY }}/${{ env.ORG }}/cpp-${{ matrix.image }}:sha-${{ github.sha }} + cache-from: type=gha,scope=cpp-${{ matrix.image }} + cache-to: type=gha,mode=max,scope=cpp-${{ matrix.image }} + + reflection: + name: build cpp-reflection + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Resolve tag + id: meta + env: + REF_NAME: ${{ github.ref_name }} + run: | + if [ "$REF_NAME" = "main" ]; then + echo "tag=2026-05" >> "$GITHUB_OUTPUT" + else + sanitized=$(echo "$REF_NAME" | tr '/' '-') + echo "tag=branch-${sanitized}" >> "$GITHUB_OUTPUT" + fi + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build + push cpp-reflection (FROM vsavkov/clang-p2996) + uses: docker/build-push-action@v5 + with: + context: containers/reflection + push: true + # vsavkov/clang-p2996 ships per-arch tags (amd64, arm64); + # the Dockerfile uses TARGETARCH (auto-set by buildx) to + # pick the right one per platform. Multi-arch manifest emerges. + platforms: linux/amd64,linux/arm64 + tags: | + ${{ env.REGISTRY }}/${{ env.ORG }}/cpp-reflection:${{ steps.meta.outputs.tag }} + ${{ env.REGISTRY }}/${{ env.ORG }}/cpp-reflection:sha-${{ github.sha }} + cache-from: type=gha,scope=cpp-reflection + cache-to: type=gha,mode=max,scope=cpp-reflection diff --git a/containers/README.md b/containers/README.md new file mode 100644 index 0000000..0942ea3 --- /dev/null +++ b/containers/README.md @@ -0,0 +1,86 @@ +# wro.cpp toolset containers + +Pre-built Docker images that back the "Reproduce locally" affordances +on every wro.cpp toolset page (https://wrocpp.github.io/toolset/). + +## Image map + +| Image | Purpose | Base | +|---|---|---| +| `ghcr.io/wrocpp/cpp-base` | Shared substrate. Ubuntu 24.04 + clang-19 + GCC 14 + CMake + Ninja + Python3. | `ubuntu:24.04` | +| `ghcr.io/wrocpp/cpp-safety` | Safety-cluster `Today` demos: sanitizers (ASan/UBSan/TSan/MSan/HWASan), AFL++, libFuzzer, clang-tidy. | `cpp-base` | +| `ghcr.io/wrocpp/cpp-performance` | Performance-cluster `Today` demos: perf, Tracy server + capture, Perfetto SDK, google-benchmark. | `cpp-base` | +| `ghcr.io/wrocpp/cpp-security` | Security-cluster `Today` demos: vcpkg, Conan, syft (SBOM), grype (CVE scan), libsodium-dev, OpenSSL dev headers. | `cpp-base` | +| `ghcr.io/wrocpp/cpp-compliance` | Compliance-cluster `Today` demos: cppcheck-MISRA, clang-tidy MISRA / AUTOSAR / CERT-C++ check sets. | `cpp-base` | +| `ghcr.io/wrocpp/cpp-reflection` | Powers every `Reflection today` triptych sub-section across all clusters. clang-p2996 fork ready to use. | `vsavkov/clang-p2996` (community-maintained) | + +## Tag scheme + +- `2026-05` -- rolling release. What the toolset pages link to. +- `2026-05-DD` -- quarterly immutable snapshot. Pinned in + `containers/scripts/*.sh` for reproducibility. +- `sha-` -- per-build snapshot. Debugging only. +- `branch-` -- per-feature-branch builds for PR review. + +## Pull + +```bash +docker pull ghcr.io/wrocpp/cpp-base:2026-05 +docker pull ghcr.io/wrocpp/cpp-safety:2026-05 +docker pull ghcr.io/wrocpp/cpp-performance:2026-05 +docker pull ghcr.io/wrocpp/cpp-security:2026-05 +docker pull ghcr.io/wrocpp/cpp-compliance:2026-05 +docker pull ghcr.io/wrocpp/cpp-reflection:2026-05 +``` + +All images are public; no `docker login` required. + +## Run + +The standard pattern is to mount the current directory and pick the +right image for the task: + +```bash +docker run --rm -it \ + -v "$PWD":/work -w /work \ + ghcr.io/wrocpp/cpp-safety:2026-05 \ + containers/scripts/run-asan.sh examples/use-after-free.cpp +``` + +Each toolset page (https://wrocpp.github.io/toolset/) carries an +embedded `docker run` block under "Reproduce locally". Copy + paste. + +## Build local (for container development) + +GitHub Actions builds + pushes to GHCR on every push to `main` (and +on any push touching `containers/`). To iterate locally: + +```bash +docker buildx build --load -t cpp-base:local containers/base +docker buildx build --load -t cpp-safety:local --build-arg BASE=cpp-base:local containers/safety +# ... etc +``` + +See `.github/workflows/build-containers.yml` for the canonical build +recipe. + +## Why two compiler families? + +- **clang-19 + GCC 14 in cpp-base** -- the cluster containers (safety, + performance, security, compliance) deal with sanitizers, profilers, + supply chain, MISRA checkers. None of these need C++26 reflection. + Apt-installable compilers keep the build fast. +- **clang-p2996 in cpp-reflection** -- every triptych sub-section's + `Reflection today` demo uses the Bloomberg fork. We pull it as a + community-maintained pre-built (`vsavkov/clang-p2996`) instead of + building it ourselves; building from source takes 45-90 minutes per + CI run. + +If you need GCC 16.1 (released April 2026, ships C++26 reflection), +godbolt's `g161` toolchain has it. We do not bake it into a container +yet; once it lands in `apt` for Ubuntu LTS, we'll add it to cpp-base. + +## Scripts + +`containers/scripts/run-*.sh` are tiny wrappers that the toolset +DockerRun blocks invoke. They keep the per-page command short. diff --git a/containers/base/Dockerfile b/containers/base/Dockerfile new file mode 100644 index 0000000..85d6289 --- /dev/null +++ b/containers/base/Dockerfile @@ -0,0 +1,52 @@ +# cpp-base: shared substrate for the wro.cpp toolset cluster containers. +# +# What's in here: Ubuntu 24.04 + apt clang-19 + apt GCC 14 (current LTS +# default) + CMake + Ninja + Python3. Designed to be small and fast to +# build (<5 min on GH Actions) so cluster images that FROM cpp-base +# stay equally cheap. +# +# What's NOT in here: clang-p2996 (reflection -- that lives in +# cpp-reflection, FROM vsavkov/clang-p2996). The toolset cluster pages +# (safety, performance, security, compliance) do not need P2996 for +# their "Today" demos; reflection-augmented examples use cpp-reflection +# instead. +# +# See containers/README.md for the full image map. + +FROM ubuntu:24.04 + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + clang-19 \ + clang-tools-19 \ + clang-tidy-19 \ + clang-format-19 \ + lld-19 \ + libc++-19-dev \ + libc++abi-19-dev \ + gcc \ + g++ \ + cmake \ + ninja-build \ + make \ + git \ + python3 \ + python3-pip \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# Symlink versioned clang to bare names so user examples just work. +RUN ln -sf /usr/bin/clang-19 /usr/local/bin/clang \ + && ln -sf /usr/bin/clang++-19 /usr/local/bin/clang++ \ + && ln -sf /usr/bin/clang-tidy-19 /usr/local/bin/clang-tidy \ + && ln -sf /usr/bin/clang-format-19 /usr/local/bin/clang-format \ + && ln -sf /usr/bin/lld-19 /usr/local/bin/lld + +LABEL org.opencontainers.image.source="https://github.com/wrocpp/cpp26-reflection-examples" +LABEL org.opencontainers.image.description="wro.cpp shared substrate: Ubuntu 24.04 + clang-19 + GCC 14 + CMake + Ninja" +LABEL org.opencontainers.image.licenses="MIT" + +WORKDIR /work +CMD ["/bin/bash"] diff --git a/containers/compliance/Dockerfile b/containers/compliance/Dockerfile new file mode 100644 index 0000000..0e3e670 --- /dev/null +++ b/containers/compliance/Dockerfile @@ -0,0 +1,29 @@ +# cpp-compliance: compliance-cluster `Today` demos. +# What's added on top of cpp-base: cppcheck (with bundled MISRA addon), +# clang-tidy MISRA / AUTOSAR / CERT-C++ checksets (clang-19 already +# carries the engine; the checksets are configured here). No qualified +# vendor compilers (GHS, IAR) -- those are proprietary; the toolset +# page documents how to point your CI at them. + +ARG BASE_TAG=2026-05 +FROM ghcr.io/wrocpp/cpp-base:${BASE_TAG} + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + cppcheck \ + && rm -rf /var/lib/apt/lists/* + +# Ship a default .clang-tidy preset that turns on the MISRA / AUTOSAR / +# CERT-C++ checksets. Containers/scripts/run-misra.sh + run-cert.sh +# point at this file by default. +COPY clang-tidy-misra.yml /opt/wrocpp/clang-tidy-misra.yml +COPY clang-tidy-cert.yml /opt/wrocpp/clang-tidy-cert.yml +COPY cppcheck-misra.cfg /opt/wrocpp/cppcheck-misra.cfg + +LABEL org.opencontainers.image.source="https://github.com/wrocpp/cpp26-reflection-examples" +LABEL org.opencontainers.image.description="wro.cpp compliance cluster: cppcheck-MISRA, clang-tidy MISRA / AUTOSAR / CERT-C++ presets on cpp-base." +LABEL org.opencontainers.image.licenses="MIT" + +WORKDIR /work +CMD ["/bin/bash"] diff --git a/containers/compliance/clang-tidy-cert.yml b/containers/compliance/clang-tidy-cert.yml new file mode 100644 index 0000000..bd05516 --- /dev/null +++ b/containers/compliance/clang-tidy-cert.yml @@ -0,0 +1,14 @@ +# clang-tidy preset for the wro.cpp compliance cluster's +# SEI CERT C++ demos. The cert-* checks are clang-tidy's first-party +# implementation of CERT C / C++ secure coding rules. +Checks: > + -*, + cert-*, + bugprone-undefined-memory-manipulation, + bugprone-use-after-move, + cppcoreguidelines-no-malloc, + cppcoreguidelines-owning-memory + +WarningsAsErrors: 'cert-*' +HeaderFilterRegex: '.*' +FormatStyle: file diff --git a/containers/compliance/clang-tidy-misra.yml b/containers/compliance/clang-tidy-misra.yml new file mode 100644 index 0000000..84c9053 --- /dev/null +++ b/containers/compliance/clang-tidy-misra.yml @@ -0,0 +1,20 @@ +# clang-tidy preset for the wro.cpp compliance cluster's MISRA C++ demos. +# Enables the closest open-source clang-tidy checks that map onto +# MISRA C++ rules. Not a substitute for a qualified MISRA tool; +# documents intent + provides reproducible CI starter. +Checks: > + -*, + bugprone-*, + cert-*, + cppcoreguidelines-*, + hicpp-*, + misc-*, + modernize-avoid-c-arrays, + modernize-avoid-bind, + modernize-deprecated-headers, + performance-*, + readability-* + +WarningsAsErrors: '' +HeaderFilterRegex: '.*' +FormatStyle: file diff --git a/containers/compliance/cppcheck-misra.cfg b/containers/compliance/cppcheck-misra.cfg new file mode 100644 index 0000000..318edd9 --- /dev/null +++ b/containers/compliance/cppcheck-misra.cfg @@ -0,0 +1,10 @@ +# cppcheck MISRA addon configuration. The MISRA rule text is NOT +# distributed with cppcheck (licensing); this config wires up the +# addon and sets the default rule severity. Readers add their own +# misra_rules.txt next to it for full text expansion. +{ + "script": "misra.py", + "args": [ + "--rule-texts=/opt/wrocpp/misra_rules.txt" + ] +} diff --git a/containers/performance/Dockerfile b/containers/performance/Dockerfile new file mode 100644 index 0000000..3051017 --- /dev/null +++ b/containers/performance/Dockerfile @@ -0,0 +1,28 @@ +# cpp-performance: performance-cluster `Today` demos. +# What's added on top of cpp-base: linux-tools (perf), google-benchmark +# headers + library, hyperfine for quick microbenchmarks. Tracy server +# and Perfetto SDK are NOT bundled (they involve heavy GUI/build deps); +# the toolset page links the upstream projects + ships a tiny +# `containers/scripts/run-perf.sh` for the apt-installable `perf`. + +ARG BASE_TAG=2026-05 +FROM ghcr.io/wrocpp/cpp-base:${BASE_TAG} + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + linux-tools-generic \ + libbenchmark-dev \ + hyperfine \ + && rm -rf /var/lib/apt/lists/* + +# Ensure `perf` is on PATH (linux-tools-generic installs as +# /usr/lib/linux-tools//perf and symlinks via update-alternatives). +RUN ln -sf /usr/lib/linux-tools-generic/perf /usr/local/bin/perf 2>/dev/null || true + +LABEL org.opencontainers.image.source="https://github.com/wrocpp/cpp26-reflection-examples" +LABEL org.opencontainers.image.description="wro.cpp performance cluster: perf, google-benchmark, hyperfine on cpp-base." +LABEL org.opencontainers.image.licenses="MIT" + +WORKDIR /work +CMD ["/bin/bash"] diff --git a/containers/reflection/Dockerfile b/containers/reflection/Dockerfile new file mode 100644 index 0000000..1d61579 --- /dev/null +++ b/containers/reflection/Dockerfile @@ -0,0 +1,34 @@ +# cpp-reflection: powers every "Reflection today" triptych sub-section. +# FROM the community-maintained vsavkov/clang-p2996 image. We add only +# the small extras readers expect from a wro.cpp container: cmake + +# ninja for project builds, plus the python helper used by the publish +# pipeline. +# +# Why community-maintained: building clang-p2996 from source takes +# 45-90 minutes per CI run. vsavkov/clang-p2996 is a thin pre-built +# layer over the Bloomberg fork. If that image becomes stale or +# unmaintained, we'd switch to a self-built source layer (see the +# top-level Dockerfile in this repo for the source recipe). + +# vsavkov ships per-arch tags (amd64, arm64), not a multi-arch manifest. +# TARGETARCH is auto-set by buildx during multi-arch builds (amd64 or +# arm64), so this Dockerfile builds for both platforms. +ARG TARGETARCH +FROM vsavkov/clang-p2996:${TARGETARCH} + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + cmake \ + ninja-build \ + python3 \ + ca-certificates \ + git \ + && rm -rf /var/lib/apt/lists/* + +LABEL org.opencontainers.image.source="https://github.com/wrocpp/cpp26-reflection-examples" +LABEL org.opencontainers.image.description="wro.cpp reflection container: clang-p2996 + cmake + ninja. Powers Reflection today demos." +LABEL org.opencontainers.image.licenses="MIT" + +WORKDIR /work +CMD ["/bin/bash"] diff --git a/containers/safety/Dockerfile b/containers/safety/Dockerfile new file mode 100644 index 0000000..f2ea3f7 --- /dev/null +++ b/containers/safety/Dockerfile @@ -0,0 +1,23 @@ +# cpp-safety: safety-cluster `Today` demos. +# What's added on top of cpp-base: AFL++ fuzzer, libFuzzer integration +# helpers, libstdc++ debug variant, additional sanitizer runtime symbols. +# (clang-tidy + sanitizers themselves already ship in cpp-base via +# clang-19.) + +ARG BASE_TAG=2026-05 +FROM ghcr.io/wrocpp/cpp-base:${BASE_TAG} + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + afl++ \ + libstdc++-13-dev \ + valgrind \ + && rm -rf /var/lib/apt/lists/* + +LABEL org.opencontainers.image.source="https://github.com/wrocpp/cpp26-reflection-examples" +LABEL org.opencontainers.image.description="wro.cpp safety cluster: AFL++, libFuzzer, sanitizers, valgrind on cpp-base." +LABEL org.opencontainers.image.licenses="MIT" + +WORKDIR /work +CMD ["/bin/bash"] diff --git a/containers/security/Dockerfile b/containers/security/Dockerfile new file mode 100644 index 0000000..a868cd7 --- /dev/null +++ b/containers/security/Dockerfile @@ -0,0 +1,41 @@ +# cpp-security: security-cluster `Today` demos. +# What's added on top of cpp-base: OpenSSL + libsodium dev headers, +# Conan (pip), syft + grype (curl installers from Anchore), vcpkg +# (git clone + bootstrap). Heavyweight tools intentionally fetched +# at image-build time so reader pulls are quick. + +ARG BASE_TAG=2026-05 +FROM ghcr.io/wrocpp/cpp-base:${BASE_TAG} + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + libssl-dev \ + libsodium-dev \ + zip \ + unzip \ + tar \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +# Conan via pip (not apt; apt version lags). +RUN pip3 install --break-system-packages --no-cache-dir conan + +# syft + grype from Anchore. Pinned via the install script's CHECK_SHA +# pattern when published; for now pull latest stable. +RUN curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin \ + && curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin + +# vcpkg: clone shallow + bootstrap. Installed under /opt so it survives +# bind-mounted /work. Reader sets VCPKG_ROOT or runs /opt/vcpkg/vcpkg. +RUN git clone --depth=1 https://github.com/microsoft/vcpkg.git /opt/vcpkg \ + && /opt/vcpkg/bootstrap-vcpkg.sh -disableMetrics +ENV VCPKG_ROOT=/opt/vcpkg +ENV PATH=/opt/vcpkg:$PATH + +LABEL org.opencontainers.image.source="https://github.com/wrocpp/cpp26-reflection-examples" +LABEL org.opencontainers.image.description="wro.cpp security cluster: vcpkg, Conan, syft, grype, libsodium, OpenSSL on cpp-base." +LABEL org.opencontainers.image.licenses="MIT" + +WORKDIR /work +CMD ["/bin/bash"]