Skip to content

CI Pipeline

GitHub Actions runs all jobs in parallel on every push and pull request to main.

Jobs

Job names follow the pattern <category>-<platform>-<toolchain-or-feature> so additional compilers and BDD targets (e.g. an ARM cross-compile or FreeRTOS QEMU oracle) slot in without renaming what's already there.

Job Preset Notes
build-linux-gcc debug Test results annotated on PR
build-linux-clang clang-debug Second compiler check using Clang 19
build-windows-msvc msvc-debug MSVC build on windows-latest; CppUTest via vcpkg; test results annotated on PR
sanitize-linux-gcc sanitize ASan + UBSan — test results annotated on PR
coverage-linux-gcc coverage Summary in Actions UI; HTML report uploaded as a downloadable run artifact
analyze-tidy tidy clang-tidy — pass/fail with errors in job log
analyze-cppcheck cppcheck cppcheck static analysis
analyze-codeql CodeQL over the library as a consumer builds it; findings in Security → Code scanning. Its own workflow (codeql.yml) — see Code scanning
analyze-format clang-format dry-run; fails if any file needs reformatting
analyze-iwyu iwyu include-what-you-use; fails on missing or unused #include directives. Advisory — runs continue-on-error
analyze-tidy-freertos-plustcp tidy clang-tidy over the FreeRTOS / FreeRTOS-Plus-TCP / Mbed TLS / Plus-FAT trees, which the base analyze-tidy lane cannot reach
analyze-tidy-freertos-lwip tidy The same, over the lwIP and ChaN FatFs trees
analyze-iwyu-freertos-plustcp iwyu IWYU over the same FreeRTOS-Plus-TCP set. Advisory
analyze-iwyu-freertos-lwip iwyu IWYU over the same lwIP set. Advisory
analyze-markdown markdownlint-cli2 over every tracked .md file
analyze-spdx scripts/check_spdx_headers.py: every file under Core/ and Platform/ carries the licence header LICENSE.md declares, and none claims anyone else's copyright
integration-linux-openssl debug Runs the in-process TLS integration tests against libssl (no network oracle)
integration-linux-mbedtls debug The same integration tests against Mbed TLS, exercising SolidSyslogMbedTlsStream and the Mbed TLS security policies
integration-windows-openssl msvc-debug Same TLS integration tests on windows-latest against libssl from vcpkg
build-linux-c89-headers Compiles every public header standalone as ISO C89 with -pedantic-errors, via scripts/check_headers_c89.py. Also proves each header is self-contained, since a header needing a companion first fails here
build-linux-c99 c99, c99-platforms Builds Core alone at strict -std=c99 (CMAKE_C_EXTENSIONS=OFF, no tests), then the POSIX and OpenSSL packs at C99 as a drift check. Proves the C99 conformance claim per PR
build-linux-tunable-override tunable-override-debug Builds against a user tunables header to prove SOLIDSYSLOG_USER_TUNABLES_FILE overrides the defaults. Also builds the Linux BDD target, for the reason under @requires_message_size_1500
bdd-linux-syslog-ng End-to-end BDD test via Docker Compose (syslog-ng-linux + behave-linux), Linux runner
bdd-windows-otel Windows-eligible BDD scenarios driven against an OTel Collector oracle
build-freertos-host-tdd-plustcp debug Host-TDD of the FreeRTOS, FreeRTOS-Plus-TCP, Plus-FAT, FatFs and Mbed TLS adapters against fakes; runs inside cpputest-freertos (upstream sources at fixed paths)
build-freertos-target-plustcp freertos-cross ARM cross-build (Cortex-M3, mps2-an385) of the BDD target ELF over FreeRTOS-Plus-TCP; uploads it as an artifact
build-freertos-target-lwip freertos-cross-lwip The same cross-build over lwIP with ChaN FatFs (FreeRtos;LwipRaw;MbedTls;FatFs;StdAtomic)
bdd-freertos-qemu-plustcp Pulls the Plus-TCP target ELF, brings up the freertos compose pair (syslog-ng-freertos + behave-freertos); Behave drives the target through qemu-system-arm's UART
bdd-freertos-qemu-lwip The same scenarios against the lwIP target ELF
consumer-smoke-linux Builds ci/consumer-smoke/ as a FetchContent consumer, proving the documented integration path still works
consumer-smoke-freertos-cross The same consumer project cross-compiled for ARM with LwipRaw;FreeRtos
verify-manifest Regenerates the Core and per-platform source manifests and fails if they differ from the committed ones
docs-build Builds the MkDocs + mkdoxy site with mkdocs build --strict; on main, deploy-docs-pages publishes it to GitHub Pages
summary Aggregates the JUnit artifacts into a run summary. Declared if: always() and asserts nothing about the other jobs' results

Branch protection

Every job in ci.yml is a required status check except deploy-docs-pages, which only runs on main, and so are the two contexts code scanning contributes — analyze-codeql and CodeQL. A pull request cannot be merged unless all of them pass. Direct pushes to main are blocked. Squash merge only.

Two qualifications on what "required" buys. The analyze-iwyu* lanes run continue-on-error, so they are required contexts that report success whatever IWYU finds — required in form, advisory in substance. And feeding the summary aggregator does not make a lane blocking: summary is declared if: always() and asserts nothing about needs.*.result, so a new lane gates merges only once its own context is added to the required list.

What each lane exercises

The lane names say the platform and toolchain but not the adapter, so:

Adapter Where it is exercised
OpenSSL (SolidSyslogOpenSslStream, security policies) integration-linux-openssl, integration-windows-openssl against real libssl
Mbed TLS (SolidSyslogMbedTlsStream, security policies) integration-linux-mbedtls against real Mbed TLS; both FreeRTOS QEMU BDD lanes over a real handshake
FreeRTOS-Plus-TCP build-freertos-host-tdd-plustcp against fakes; bdd-freertos-qemu-plustcp end to end under QEMU
lwIP bdd-freertos-qemu-lwip end to end under QEMU; static analysis via the *-freertos-lwip lanes
ChaN FatFs Built and analysed in the lwIP lanes; store-and-forward scenarios run in bdd-freertos-qemu-lwip
FreeRTOS-Plus-FAT Host-TDD against fakes in build-freertos-host-tdd-plustcp, and built in the Plus-TCP cross lanes
POSIX, Windows The build-linux-* and build-windows-msvc lanes, plus both host BDD lanes

Code scanning

codeql.yml runs CodeQL over the C sources on every push and pull request to main, and weekly so that queries GitHub ships later are applied to unchanged code. Findings appear under Security → Code scanning, not in the job log.

CodeQL analyses what the compiler compiled and nothing else, so the build it observes is the analysis scope. The lane builds ci/consumer-smoke/ — the same FetchContent consumer documented in Adding it to your build — so the code analysed is the code an integrator compiles. Consuming the library as a subproject also scopes the database without any bespoke flags: SOLIDSYSLOG_IS_TOP_LEVEL is false, so the unit tests and the BDD targets are never configured.

SOLIDSYSLOG_PLATFORMS is named explicitly rather than left to auto-detection, because the list is authoritative: a platform it does not name is silently absent from the build, and therefore from the analysis. An assertion step then fails the lane unless every named platform produced compiled objects — proving what the compiler emitted rather than what the configure step was asked for, since a platform that is selected but contributes no sources would satisfy any check of the selection itself.

Triage follows the support tiers rather than the reported severity:

  • Tier 1 Core/ — treat as blocking; this is the shipped product.
  • Tier 2 Platform/ — fix on merit; a finding here does not hold up a release.

Accepted findings are dismissed in the Security tab with a reason, not suppressed with in-source comments. The C sources already carry clang-tidy, cppcheck-MISRA and IWYU suppression dialects, and a fourth would cost more in readability than it returns.

Code scanning contributes two required contexts, and both are needed:

  • analyze-codeql — the Actions job. Proves the analysis ran and uploaded, so a lane that breaks or stops running blocks the merge rather than passing silently.
  • CodeQL — the code-scanning results check. This is the one that fails when a pull request introduces a new alert. Without it, a PR could add findings and still merge green because the job itself succeeded.

Both were made required on a clean baseline: security-extended produced no alerts over the consumer build it analyses, so nothing had to be grandfathered in.

A clean baseline is not the same as an absence of vulnerabilities. Much of security-extended is taint tracking, and this database offers it no source: Core/ makes no library read calls of its own, and everything arrives through a vtable that CodeQL's dataflow does not follow. Those queries cannot fire whatever the code does. Read the result as no local defects found — buffer arithmetic, conversions, comparisons, unchecked returns — rather than as proof that no injection or overflow path exists.

Release automation

release-please runs on every push to main. It reads commit messages (which must follow Conventional Commits) and maintains a release PR that bumps the version and updates CHANGELOG.md. Merging that PR creates a GitHub Release and tag.

Permissions

Each job is granted only the permissions it needs. Every workflow declares permissions: contents: read at the top level and no workflow grants a write scope there, so a job added later starts read-only rather than inheriting a write token by default. Write scopes are held by the jobs that need them: jobs publishing test results add checks: write and pull-requests: write; deploy-docs-pages adds pages: write and id-token: write to publish the documentation site to GitHub Pages; analyze-codeql adds security-events: write to upload its results to the Security tab; sbom.yml's publish job adds contents: write and id-token: write to attach keyless-signed assets to a release; and release-please.yml's job adds contents: write and pull-requests: write to maintain the release PR and create the tag.