Integrating Verification into CI¶
Once osqar_project.json defines the verification plan and you have a working
build-and-test.sh, the next step is wiring everything into an automated CI
pipeline that produces auditable evidence on every commit.
This guide uses the OSQAr-cJSON project as the reference implementation — its shell script + GitHub Actions workflow combination runs the full qualification lifecycle on every push and produces a SHA256SUMS-manifested shipment on every version tag.
Architecture¶
The verification pipeline has three layers:
Shell script (
build-and-test.sh) — the single entry point that runs the actual tools: compiler, test suite, sanitizers, lcov, lizard, cppcheck. It accepts subcommands (build,test,sanitizer,coverage,complexity,static-analysis,all) so individual phases can be iterated on locally without running the full pipeline.CI workflow (
.github/workflows/ci.yml) — callsbuild-and-test.sh all, builds Sphinx documentation, runs traceability checks, assembles the shipment, generates checksums, packages the ZIP, and creates a GitHub Release on tags. Optionally deploys Sphinx HTML to GitHub Pages.OSQAr CLI (
osqar traceability,osqar checksum,osqar shipment) — invoked by CI for traceability verification, integrity manifests, and shipment packaging.
A reusable CI workflow template is included in the OSQAr repository at
templates/ci-qualification.yml.
Step 1: Write build-and-test.sh¶
The shell script is the heart of the pipeline. It must:
Use the qualified toolchain consistently (same compiler, flags, language standard).
Compile the actual library source — not a demo or stub program.
Run the real test suite and emit JUnit XML parsed from the test runner output.
Run sanitizer-instrumented tests (ASan+UBSan) and capture their logs.
Measure coverage with
--coverage+lcov, aggregating across all test binaries (see Coverage aggregation across test binaries below).Run
lizardfor cyclomatic complexity analysis, capturing raw output.Run
cppcheckfor static analysis, capturing findings as XML.Run a compiler warning audit with
-Werrorand capture stderr.Accept subcommands so individual phases can be iterated on quickly.
Exit non-zero on any failure (
set -euo pipefail).
Reference: OSQAr-cJSON build-and-test.sh (411 lines, covers all verification activities relevant for an ISO 26262 SEooC qualification attempt targeting ASIL D).
Step 2: Wire the CI workflow¶
Copy templates/ci-qualification.yml from the OSQAr repository to
.github/workflows/ci.yml in your qualification project.
Replace the <PLACEHOLDER> tokens:
Placeholder |
Description |
Example (cJSON) |
|---|---|---|
|
Short project identifier |
|
|
Additional |
|
|
Test need ID prefix |
|
|
Implementation need ID prefix |
|
|
Pinned OSQAr CLI version |
|
|
Shell commands to copy source into shipment |
|
The workflow triggers on pushes to main and on tags matching
<libversion>-<osqarversion> (e.g., 1.7.19-0.9.0).
CI job summary¶
The qualify job runs these steps in order:
Checkout — source code plus submodules.
Install tool dependencies —
aptpackages,pippackages (lizard, coverage), Sphinx doc dependencies.Install OSQAr CLI — pinned version via
pip.Build and test —
./build-and-test.sh all, which producestest_results.xml,coverage_report.txt,complexity_report.txt, and sanitizer/static-analysis evidence.Build Sphinx documentation —
sphinx-build -b html -W, verifying thatneeds.jsonis produced.Upload Sphinx HTML artifact — for later Pages deployment.
Run OSQAr doctor — diagnostic overview (non-fatal).
Traceability check —
osqar traceabilitywith custom prefix overrides.Assemble shipment — copy documentation, source code, reports, and verification evidence into
_shipment/.Generate and verify checksums —
osqar checksum generate+osqar checksum verify.Package shipment —
osqar shipment package→ ZIP.Upload shipment artifact — 90-day retention.
Create GitHub Release (on tag) — uploads the shipment ZIP and SHA256SUMS to the release page.
Step 3: Configure shipment assembly¶
The “Assemble shipment” step copies artifacts into a structured _shipment/
directory:
_shipment/
├── docs/ # Sphinx HTML build
├── implementation/ # Source files, osqar_project.json, build-and-test.sh
├── tests/ # Test suite source (for reproducibility)
├── reports/ # test_results.xml, coverage_report.txt, complexity_report.txt
├── verification/ # Sanitizer logs, cppcheck XML, compiler warning audit
└── SHA256SUMS # Integrity manifest (generated by osqar checksum)
Every file is checksummed into SHA256SUMS. The shipment ZIP is the
auditable evidence package — it contains everything an auditor needs to
reproduce and verify the qualification.
Step 4: Verify the pipeline locally¶
Before pushing to CI, verify the full pipeline end-to-end:
# Run the full verification pipeline
./build-and-test.sh all
# All three evidence artifacts must exist and contain real data
test -s test_results.xml || echo "MISSING: test_results.xml"
test -s coverage_report.txt || echo "MISSING: coverage_report.txt"
test -s complexity_report.txt || echo "MISSING: complexity_report.txt"
# Build docs and run traceability
python3 -m sphinx -b html -W . _build/html
osqar traceability _build/html/needs.json \
--test-prefix VER_ --code-prefix IMPL_
# Assemble, checksum, and verify the shipment (simulates CI)
osqar shipment prepare --project . --skip-verification
osqar checksum generate --root _shipment --output _shipment/SHA256SUMS
osqar checksum verify --root _shipment --manifest _shipment/SHA256SUMS
Important
Every evidence artifact (test_results.xml, coverage_report.txt,
complexity_report.txt) must contain live measurement data from actual
tool runs. Never use shell heredocs with hand-written numbers claiming fake
coverage or test counts. Verify each file was produced by parsing real tool
output before committing the CI workflow.
Step 5: Create a version tag¶
When the pipeline passes, tag a release to trigger the automated shipment:
git tag -a "1.7.19-0.9.0" -m "Library v1.7.19 — OSQAr v0.9.0 qualification"
git push origin "1.7.19-0.9.0"
CI runs on the tag push, assembles the shipment, and creates a GitHub Release with the shipment ZIP and SHA256SUMS attached.
Reference implementation¶
OSQAr-cJSON is the canonical reference for a complete verification pipeline:
162 Unity tests across 21 executables, zero failures
88.1% statement coverage aggregated via
lcovacross all test binariesZero compiler warnings with
-Werror -Wall -Wextra -WconversionASan+UBSan clean on the full test suite
Zero traceability violations (custom prefixes:
VER_,IMPL_)SHA256SUMS-manifested shipment ZIP on every
1.7.19-X.Y.ZtagGitHub Pages with full Sphinx HTML documentation at bitvortex.github.io/OSQAr-cJSON
Key files to study:
ci.yml — CI workflow (199 lines)
build-and-test.sh — verification pipeline (411 lines)
osqar_project.json — project config with gap definitions
conf.py — Sphinx config with PlantUML and custom need prefixes
Common CI pitfalls¶
- PlantUML command vs server
Setting
plantuml = "https://..."inconf.pycauses Sphinx to try toexec()the URL. Useplantuml_serverfor web URLs. The OSQArconf.pytemplate handles this automatically (see the PlantUML configuration in CLI Reference).- lizard is pip-only
The complexity analysis tool
lizardis not available viaapton Ubuntu 24.04. Install withpip install lizardin the CI workflow. All other Python dependencies (Sphinx, sphinx-needs, sphinxcontrib-plantuml, furo, pyyaml) are pulled transitively by installing OSQAr itself.- PlantUML
!themeon older CI runners Ubuntu’s
apt install plantumlships v1.2020.2, which does not support!theme. Useskinparamdirectives instead to keep diagrams portable across local and CI environments.
- Coverage aggregation across test binaries
When a library is statically linked into multiple test executables, each binary produces its own
.gcdafile. Aggregate them with:lcov --capture -d build_dir -o coverage.info lcov -e coverage.info '*/your_lib.c' -o coverage_filtered.info
Do not delete individual test binary
.gcdafiles — they contain the library coverage counters.gcovrpath-resolution fails on multi-binary projects; preferlcov.- Evidence artifacts must be live measurements
The most dangerous class of pipeline bugs is fabricated evidence: shell heredocs with hand-written numbers posing as tool output. Always verify that
test_results.xml,coverage_report.txt, andcomplexity_report.txtare produced by actual tool runs, not static text blocks.- Pages deployment environment block
Do not add an
environment:block to thepagesjob in CI —actions/deploy-pages@v4handles environment creation internally. Adding one causes the job to fail instantly with zero steps executed.
GitHub Pages setup (one-time)¶
To publish Sphinx HTML documentation via GitHub Pages on every tag:
# Enable Pages on the repo
gh api /repos/<owner>/<repo>/pages \
-X POST -f 'source[branch]=main' -f 'source[path]=/docs'
# Switch to workflow-based deployment
gh api /repos/<owner>/<repo>/pages -X PUT -f 'build_type=workflow'
The CI template includes a pages job that depends on qualify, downloads
the Sphinx HTML artifact, and deploys via actions/deploy-pages@v4.