adacovex CI/CD¶
GitHub Actions¶
The composite action at ./action.yml mirrors the base CLI. CI can drive every
assessment feature the same way the binary does.
Quick start¶
# .github/workflows/adacovex.yml
on:
push:
branches: [main]
pull_request:
jobs:
adacovex:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: bladeacer/adacovex@v1
with:
target: .
standard: all # DO-178C + ISO 26262 + IEC 62304
require-spark: Platinum
require-docstrings: 100
require-tests: 1229
run-tests: true
generate-sbom: true
This job builds adacovex. It runs the target’s native tests and the full
assessment. It gates on Platinum SPARK, 100% docstring coverage, and 900
passing tests. On failure, the job publishes adacovex-assessment artifacts
and Markdown summaries.
PR coverage gate¶
Use --coverage-delta to fail PRs that drop docstring coverage:
# .github/workflows/pr-coverage.yml
on:
pull_request:
jobs:
coverage-delta:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: bladeacer/adacovex@v1
with:
target: .
coverage-delta: ${{ github.event.pull_request.base.sha }}
Release workflow¶
Tag a release. The bundled workflow (release.yml) builds the binary and runs GNATprove. It validates the self-assessment. It publishes `adacovex-vX.
Y. Z.tar.gz and the composite action bundle. Floating tags (@latest, @v1, @v1.3) always point at the newest release. Pin @vX.
Y. Z` for reproducibility.
GitLab CI¶
The same inputs map to GitLab CI variables:
# .gitlab-ci.yml
adacovex:
image: ubuntu:latest
before_script:
- apt-get update -qq && apt-get install -y -qq curl
- curl -fsSL https://raw.githubusercontent.com/bladeacer/adacovex/main/install.sh | bash
script:
- adacovex --target=. --standard=all --dal=C --require-spark=Platinum --require-docstrings=100
artifacts:
when: always
paths:
- sbom.json
- docs/badges/*.svg
If your runner already has Alire, replace the install.sh step with
alr install covex gnatprove.
Other CI systems¶
The action is a pure composite of shell steps. It works on any runner that can
run bash and install Alire:
Install Alire + GNAT (
alr toolchain --install gnat_native).Download the release bundle or build from source.
Run
adacovexwith the same flags as the GitHub Action inputs.
The JSON API (/api/metrics when using --serve, or --emit-metrics) lets
you parse results in any language.
Action/CLI/docs parity is a feature gate. CI runs the action-parity
check (tools/check-action-parity.py), which fails when the action’s inputs
stop mirroring the base CLI option set, and also fails when the ### Inputs
table below drifts from action.yml. See tools/check-action-parity.py for
the mapping rules (maintainers run the same check locally via
make action-parity-check; see the developer guide).
Composite action (./action.yml)¶
The composite action installs Alire via
alire-project/setup-alire.
GNAT comes from gnat-version plus gprbuild. gnatprove is NOT an
alr toolchain component. The prove subcommand resolves it through the
target project’s manifest, $PATH, cached toolchain, or download.
The action obtains the version-matched adacovex binary. It downloads the
release bundle by default. You can set build: true to build from source.
Optionally, the action runs GNATprove (prove) and the native tests
(run-tests). Then it runs the assessment. It generates a proof-aware SBOM
(generate-sbom, default true). It publishes a Markdown step summary,
machine-readable outputs, and SVG badge artifacts.
run-tests builds the target’s native test suite first. In a consumer
workspace, the build: true step builds adacovex in a scratch checkout. It
leaves the target untouched.
The action runs alr build in the target root before executing
test-command. The target root is a subdirectory when target points at one.
In the self-assessment case, the build is an incremental no-op.
This design keeps test-command: ./test_crdt-style usage working in consumer
repositories such as Ada_CRDT’s release workflow.
The action is version-matched to the adacovex binary. The release workflow bundles adacovex-vX. Y. Z.tar.gz for every `vX.
Y. Z` tag. The action downloads the binary for the tag it is referenced by. Reference it by a floating tag to always get the latest published release.
You can pin to an exact release for reproducibility:
steps:
- uses: actions/checkout@v7
- uses: bladeacer/adacovex@v1
with:
target: .
standard: all # badges/reports for DO-178C + ISO 26262 + IEC 62304
Inputs¶
Input |
Default |
Description |
|---|---|---|
|
|
Target project root (relative to workspace root) |
|
|
DO-178C DAL level to assess (A-E); also the shared rigor tier |
|
|
Compliance standard: |
|
|
ISO 26262 ASIL level (A-D, QM); sets the standard and tier |
|
|
IEC 62304 safety class (A-C); sets the standard and tier |
|
|
GNAT toolchain version to select via |
|
|
adacovex version; defaults to the tag the action is referenced by |
|
|
Build adacovex from source instead of downloading the version-matched binary |
|
|
Run GNATprove before assessing, for repos that don’t commit |
|
|
Minimum SPARK level (Stone..Platinum); CI threshold gate |
|
|
Minimum docstring coverage % (0-100); CI threshold gate |
|
|
Minimum passing test count; CI threshold gate |
|
|
Minimum proved-VC coverage % (0-100); CI threshold gate |
|
|
Build the target’s native test suite and run it (requires |
|
|
Command (relative to workspace root) that runs the target’s test suite |
|
|
Pass |
|
|
Run the assessment and publish outputs/badges ( |
|
|
Git ref to run |
|
|
Git ref to run |
|
|
Write |
|
|
Write a JSON export of assessment metrics + the dependency graph to this file |
|
|
Generate a proof-aware SBOM after the assessment and upload it |
|
|
SBOM format: |
|
|
Cache Alire toolchain/deps with |
|
|
Persist adacovex’s on-disk result cache across runs |
|
|
Override the target project manifest path |
|
|
Suppress SVG badge generation |
|
|
Disable strict mode (skip dirs, no patches) |
|
|
Directory name to skip in relaxed mode (repeatable, comma-separated) |
|
|
Verbose diagnostics on stderr |
|
|
Disable adacovex on-disk result caching |
|
|
Override adacovex result cache directory |
|
|
Max adacovex result-cache entries before eviction |
|
|
GNATprove parallelism for the |
|
|
GNATprove proof effort 0-4 |
|
|
GNATprove per-check prover timeout in seconds |
|
|
GNATprove max proof steps |
|
|
GNATprove prover memory limit in MB |
|
|
Force full GNATprove reanalysis ( |
|
|
Disable GNATprove automatic loop unrolling |
|
|
Disable GNATprove contextual-analysis inlining |
|
|
Suppress GNATprove benign info messages (the default set) from prove output. Quiet is already the default for local runs; this is the explicit prove-mode form. |
|
|
Comma-separated GNATprove info tags to suppress from prove output (for example |
Outputs¶
Output |
Description |
|---|---|
|
|
|
SPARK level detected (Stone..Platinum) |
|
Number of passing tests |
|
Current docstring coverage (in |
Result caching¶
The action restores ~/.adacovex/cache before running adacovex. It saves the
cache when the job finishes (result-cache, default true).
Every entry is keyed by its artifact’s SHA-256 content hash. Restoring a cache from an earlier run or commit is always safe. Only files that are byte-for-byte unchanged are served from it. The remaining files are rescanned and re-parsed automatically.
Workflows¶
.github/workflows/ci.yml– three jobs on push tomainand pull requests:self-assessment– build + prove + assess at--standard=all(so the DO-178C, ISO 26262, and IEC 62304 badges/reports are all emitted and gated), with the Platinum / 100% docstrings / test-count / 100% proof thresholds.adacovex-tests– build + native test suite (run-tests,assess: false).coverage-gate(push only) – runs the coverage gate, comparing docstring coverage between the latest two release tags (a maintainer step; see the developer guide).
.github/workflows/pr-check.yml– runs--coverage-deltaagainstpull_request.base.shato fail PRs that drop docstring coverage..github/workflows/release.yml– on av*tag, builds the release binary, runs GNATprove, validates the--standard=allself-assessment, and publishes the GitHub Release (see Release bundling).
Markdown summaries and loud failures¶
Every CI run leaves a Markdown summary at the bottom of the job page
($GITHUB_STEP_SUMMARY):
The composite action’s assessment step writes an
## adacovex assessmenttable. The table shows target, bundled version, compliance label, SPARK level, tests, and coverage. It also writes the full raw output.An
if: always()Write run summary step appends a run-overview table. The table shows version, target, standard, DAL, and job result.Each workflow adds a
summaryjob (if: always(),needs:all other jobs) that aggregates every job result into one table at the bottom of the run.
Diagnostics are layered so a failure is debuggable from the Actions UI without re-running locally:
The assessment output is folded into a GitHub log group (
::group::). The step result stays visible. The detail stays one click away.WARNINGlines are re-surfaced as::notice::annotations.An
adacovex-assessmentartifact (uploadedif: always()) carries the full, untruncated assessment output. It also carries the--emit-metricsJSON export whenemit-metricsis set. A flaky or unmet gate never requires a re-run to reproduce.Badge and SBOM artifacts are uploaded even when the step failed (
if: always()). Partially produced reports stay inspectable.
Threshold failures fail loudly at every layer:
Unmet
--require-*gates make the adacovex binary exit non-zero.The assessment step re-surfaces each
CI GATE:line as a GitHub::error::annotation. The annotation is visible at the top of the job page, not just in the log. The step marks the summary table FAILED with the unmet gates.The action’s
Write run summarystep (runs on failure too) reports the failed job result.The workflow
summaryjob exits1when any dependency failed. The whole run is red even if the failing job was retried. Anif: always()cleanup step does not mask this.
Debugging guide: what to do when you see …¶
Output |
Meaning |
Action |
|---|---|---|
|
Proven VCs or |
Check the |
|
Missing |
Run |
|
Some VCs unproved/justified |
Inspect |
|
A physical line > |
Split the declaration. DAL becomes |
|
Cache stats per run |
|
|
Typo |
Use |
|
Non-fatal warning surfaced from |
Download the |
|
File or function exceeds caps ( |
Run |
Better debugging output contract. ci.yml now has timeout-minutes,
concurrency.cancel-in-progress, fetch-tags: true, and actions/cache for
both toolchain and result-cache. After these brittleness fixes, every failure
leaves three things without a re-run:
The
::error/::noticeannotations at the top of the job page.The
## adacovex assessmentMarkdown table inGITHUB_STEP_SUMMARY.The
adacovex-assessmentartifact (full untruncatedadacovex.out+adacovex-metrics.jsonwhenemit-metricsis set).
When the gate is flaky, start from the artifact, not a local repro.
Release version bundling¶
The release workflow builds the binary from the vX. Y. Z tag. It bundles that version into the binary.
The action’s build step sets ADACOVEX_VERSION (from github.ref_name). It regenerates src/adacovex_version_info.ads before alr build. The shipped adacovex --version reports exactly the tag.
The download step of the published action verifies this with adacovex --version after unpacking the release bundle. Maintainers reproduce the
release locally with make release VERSION=x.y.z (see the
developer guide); a normal source build
reads the version from alire-dev.toml instead.
PR coverage gate¶
Gate every pull request so docstring coverage does not regress against the
base branch. This is exactly what --coverage-delta was built for:
# .github/workflows/pr-check.yml
on:
pull_request:
jobs:
coverage-delta:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- uses: bladeacer/adacovex@v1
with:
target: .
standard: all
coverage-delta: ${{ github.event.pull_request.base.sha }}
The action exits non-zero when coverage drops, failing the check. This
workflow ships in the repo at .github/workflows/pr-check.yml.
Release bundling¶
Every vX. Y. Z tag triggers .github/workflows/release.yml. The workflow calls the composite action with build, release-build, and prove.
It builds the release binary, runs GNATprove, and validates the self-assessment. Then it packages and publishes:
adacovex-vX.Y.Z.tar.gz– the version-matched binary (adacovexplus thecovexalias). The action downloads this asset for the tag it is referenced by, so@v1.9.0runs adacovexv1.9.0.adacovex-action-vX.Y.Z.tar.gz– a copy of the composite action itself for vendoring or air-gapped use.
Both bundles are attested with
actions/attest
on every tag. OIDC attestations appear under the release’s attestations tab.
The release notes link the signed attestation via the action’s
attestation-url output. They also link a Git Changelog compare link
(compare/v1.9.0...v1.14.0) and the human-readable changelogs.
Changelog listing. The Create GitHub Release step derives the changelog
list from the available docs/changelogs/adacovex-*.md entries. The entries
are between the previous release tag and the released version.
It resolves the previous three-component release tag
(git tag --sort=-version:refname, for example v1.9.0 when releasing
v1.14.0). Then it lists every changelog whose version is strictly above the
previous release and at or below the released version. Releasing v1.14.0
after v1.9.0 links the 1.10.0..1.14.0 changelogs in one release.
The list is emitted newest-first (version-sorted, not shell glob order).
The entries read 1.14.0 down to 1.10.0.
The list is derived from the changelog files present in the tree, not from
tags. A version that was never released has no entry. A release that skips
versions still links every changelog in the range. Each entry links the
release’s changelog page on the deployed Read the Docs site
(https://adacovex.readthedocs.io/en/latest/changelogs/adacovex-<v>.html),
not a GitHub blob URL: the manual is a Sphinx project and the changelogs are
part of the published book.
The CI release binary is Linux x86-64 only for now. The release workflow
runs on ubuntu-latest. It packages the Linux binary and the prebuilt
GNATprove toolchain bundle for that target. macOS, FreeBSD, Windows, and Linux
aarch64 build adacovex from source via Alire instead. See
Platforms.
Maintainers reproduce the release locally with make release VERSION=x.y.z
(see the developer guide): it builds
--release, generates proofs, validates DAL-C, and bundles dist/, then
tags and pushes to trigger the workflow. The bundled version is always the
build’s version (src/adacovex_version_info.ads comes from alire-dev.toml
or ADACOVEX_VERSION at build time).
Consumer manifest prerequisites (avoid a broken CI)¶
adacovex’s prove subcommand and the GitHub Action resolve gnatprove
through the target project’s manifest. The pinned gnatprove crate is
deployed via alr -n get gnatprove=<version>. It is run directly, with no
alr exec over the whole workspace.
For the command to succeed in a clean checkout or on CI, the consumer’s manifests must follow two rules:
alire.tomlmust be the clean publishing manifest. It must contain no dev tooling (gnatprove,gnatdoc_bin,gnatformat_bin,covex). It must contain no[[pins]]. Alire reads this manifest when the action runsalr build. It must resolve with nothing but Alire + GNAT.alire-dev.tomlmust declarecovexas a normal index dependency (covex = "*"). It must never be pinned to a local path such ascovex = { path = "../adacovex" }. A path pin resolves only on the machine that has the sibling checkout. In a consumer workspace or on CI, Alire fails the whole workspace load with a confusing error. This happens before adacovex oralrruns:ERROR: Failed to load alire.toml: ERROR: pins: ERROR: covex: ERROR: Pin path is not a valid directory: /home/runner/work/<repo>/<repo>/../adacovex
If you see that, drop the covex path pin. Use covex = "*". Strip the dev
deps and pins out of alire.toml. Keep them only in alire-dev.toml.
The Makefile pattern in many projects keeps the published alire.toml clean.
It swaps alire-dev.toml over alire.toml only for the duration of a
prove/fmt/doc target, then restores it. This gives local tooling the dev
deps.