| name: LibJWT Build, Unit Tests, and Coverage |
| |
| on: |
| workflow_dispatch: |
| push: |
| branches: [ "master" ] |
| paths-ignore: |
| - "doxygen/**" |
| - ".github/**" |
| - "images/**" |
| - ".gitignore" |
| - "*.md" |
| pull_request: |
| branches: [ "master" ] |
| paths-ignore: |
| - "doxygen/**" |
| - ".github/**" |
| - "images/**" |
| - ".gitignore" |
| - "*.md" |
| |
| permissions: |
| contents: read |
| |
| jobs: |
| build-macos: |
| runs-on: macos-latest |
| steps: |
| - uses: actions/checkout@v5 |
| - uses: ConorMacBride/install-package@v1 |
| with: |
| brew: gnutls openssl@3 jansson pkgconf cmake check curl bats-core jq |
| |
| - name: Build and Test |
| run: | |
| cmake -B build -DWITH_LIBCURL=YES |
| cmake --build build -- all check |
| |
| # Vendor compatibility matrix: does libjwt build + pass its tests against the |
| # OpenSSL and JSON libraries that real distros actually ship? Each distro is |
| # built twice - once with jansson, once with json-c - so both JSON backends are |
| # exercised across their shipped vendor versions (json-c 0.15/0.17/0.18, etc.). |
| # |
| # GnuTLS is OFF on every row. Every distro currently ships a GnuTLS stack |
| # (<= 3.8.12) that segfaults loading Ed448 keys and fails X25519/X448 ECDH-ES |
| # in libjwt (the NIST P-curves are fine). A GnuTLS 3.8.13 stack is correct - |
| # verified on debian:forky (apt GnuTLS 3.8.13 + nettle 3.10.2 + p11-kit passes |
| # all of them), so it is the <= 3.8.12 -> 3.8.13 GnuTLS upgrade, NOT p11-kit. |
| # No stable distro ships 3.8.13 yet; native GnuTLS is covered by |
| # build-linux-combos (image's 3.8.13). Re-enable per distro once it ships >= 3.8.13. |
| # |
| # Debian has no hosted runner, so stable/oldstable run as containers. |
| build-linux: |
| name: "Compat / ${{ matrix.distro }} / ${{ matrix.json }}" |
| runs-on: ${{ matrix.runs-on }} |
| container: ${{ matrix.container }} |
| # ubuntu-26.04 is still a public-preview runner image; don't fail all of CI |
| # if it hiccups. |
| continue-on-error: ${{ matrix.experimental || false }} |
| strategy: |
| fail-fast: false |
| matrix: |
| json: [ jansson, json-c ] |
| distro: [ ubuntu-22.04, ubuntu-24.04, ubuntu-26.04, debian-stable, debian-oldstable ] |
| exclude: |
| # Ubuntu 22.04 (jammy) ships json-c 0.15; libjwt requires json-c >= 0.16 |
| # (CMakeLists.txt), so that one combination can't configure. jammy still |
| # builds with jansson. (json-c: noble 0.17, bookworm 0.16, trixie/ |
| # resolute 0.18 all clear the floor.) |
| - { distro: ubuntu-22.04, json: json-c } |
| # Attach per-distro runner/container (and the preview flag) to each JSON |
| # variant of each distro -> 5 distros x 2 backends - 1 exclude = 9 jobs. |
| include: |
| - { distro: ubuntu-22.04, runs-on: "ubuntu-22.04", container: "" } |
| - { distro: ubuntu-24.04, runs-on: "ubuntu-24.04", container: "" } |
| - { distro: ubuntu-26.04, runs-on: "ubuntu-26.04", container: "", experimental: true } |
| - { distro: debian-stable, runs-on: "ubuntu-latest", container: "debian:stable" } |
| - { distro: debian-oldstable, runs-on: "ubuntu-latest", container: "debian:oldstable" } |
| steps: |
| # Minimal Debian containers need git before actions/checkout can clone. |
| # Hosted ubuntu runners already have it (and aren't containers). |
| - name: Bootstrap container |
| if: ${{ matrix.container != '' }} |
| run: | |
| apt-get update |
| apt-get install -y --no-install-recommends git ca-certificates sudo |
| |
| - uses: actions/checkout@v5 |
| |
| - name: Install dependencies |
| run: | |
| PKGS="build-essential cmake pkg-config libssl-dev check bats jq" |
| if [ "${{ matrix.json }}" = "json-c" ]; then |
| PKGS="$PKGS libjson-c-dev" |
| else |
| PKGS="$PKGS libjansson-dev" |
| fi |
| if [ "$(id -u)" != "0" ]; then SUDO=sudo; else SUDO=; fi |
| $SUDO apt-get update |
| $SUDO apt-get install -y --no-install-recommends $PKGS |
| |
| - name: Build and Test |
| run: | |
| JSON_FLAG="" |
| [ "${{ matrix.json }}" = "json-c" ] && JSON_FLAG="-DWITH_JSON_C=YES" |
| cmake -B build \ |
| -DWITH_OPENSSL=ON -DWITH_GNUTLS=OFF -DWITH_MBEDTLS=OFF \ |
| $JSON_FLAG |
| cmake --build build -- all check |
| |
| # OpenSSL is an optional backend, so the library must build and pass the test |
| # suite with any non-empty subset of {OpenSSL, GnuTLS, MbedTLS}. Exercise the |
| # combinations in the custom CI base image (debian:forky + GnuTLS built |
| # --with-leancrypto + latest MbedTLS 3.6.x LTS) so every backend - including |
| # the GnuTLS ML-DSA success path - is real. See .github/docker/. |
| build-linux-combos: |
| name: "Combo / ${{ matrix.name }}" |
| runs-on: ubuntu-latest |
| container: |
| image: ghcr.io/benmcollins/libjwt/gnutls-leancrypto-mbedtls:latest |
| strategy: |
| fail-fast: false |
| matrix: |
| include: |
| - { name: "MbedTLS", flags: "-DWITH_OPENSSL=OFF -DWITH_GNUTLS=OFF -DWITH_MBEDTLS=ON" } |
| - { name: "GnuTLS", flags: "-DWITH_OPENSSL=OFF -DWITH_GNUTLS=ON -DWITH_MBEDTLS=OFF" } |
| - { name: "OpenSSL", flags: "-DWITH_OPENSSL=ON -DWITH_GNUTLS=OFF -DWITH_MBEDTLS=OFF" } |
| - { name: "MbedTLS+GnuTLS", flags: "-DWITH_OPENSSL=OFF -DWITH_GNUTLS=ON -DWITH_MBEDTLS=ON" } |
| - { name: "MbedTLS+OpenSSL", flags: "-DWITH_OPENSSL=ON -DWITH_GNUTLS=OFF -DWITH_MBEDTLS=ON" } |
| - { name: "GnuTLS+OpenSSL", flags: "-DWITH_OPENSSL=ON -DWITH_GNUTLS=ON -DWITH_MBEDTLS=OFF" } |
| # The comprehensive row: all three backends + experimental ML-DSA. This |
| # is the ONLY row that collects coverage (-> Codecov) and runs memcheck. |
| # On this image GnuTLS is built --with-leancrypto, so WITH_ML_DSA=ON |
| # exercises the ML-DSA SUCCESS path on both OpenSSL and GnuTLS (and the |
| # rejection path on MbedTLS) - those lines are run, not just compiled, |
| # so they count toward Codecov instead of dragging codecov/patch down. |
| - { name: "all", flags: "-DWITH_OPENSSL=ON -DWITH_GNUTLS=ON -DWITH_MBEDTLS=ON -DWITH_ML_DSA=ON", coverage: true } |
| steps: |
| - uses: actions/checkout@v5 |
| |
| # The repo is checked out as a different owner than the container user; |
| # mark it safe so the coverage/Codecov git calls work. |
| - name: Mark workspace safe for git |
| run: git config --global --add safe.directory "$GITHUB_WORKSPACE" |
| |
| - name: "Build (${{ matrix.name }})" |
| run: | |
| cmake -B build ${{ matrix.flags }} -DWITH_LIBCURL=YES \ |
| ${{ matrix.coverage && '-DENABLE_COVERAGE=YES' || '' }} |
| cmake --build build -- all |
| |
| - name: "Test (${{ matrix.name }})" |
| if: ${{ !matrix.coverage }} |
| working-directory: ${{ github.workspace }}/build |
| run: ctest --output-on-failure |
| |
| # ---- comprehensive "all" row only: coverage + memcheck + upload ---- |
| - name: "Coverage (${{ matrix.name }})" |
| if: ${{ matrix.coverage }} |
| run: | |
| # The genhtml step of check-code-coverage exits non-zero, but the lcov |
| # capture (check-code-coverage.info) is produced and is what we upload. |
| cmake --build build -- check-code-coverage || true |
| test -f build/check-code-coverage.info |
| |
| - name: "Memcheck (${{ matrix.name }})" |
| if: ${{ matrix.coverage }} |
| working-directory: ${{ github.workspace }}/build |
| run: ctest -T memcheck |
| |
| - uses: codecov/codecov-action@v5.1.2 |
| if: ${{ matrix.coverage }} |
| with: |
| token: ${{ secrets.CODECOV_TOKEN }} |
| files: ${{ github.workspace }}/build/check-code-coverage.info |
| disable_search: true |
| verbose: true |