Baseline Management¶
ABI baselines are pre-computed snapshots of a library's ABI surface at a known-good point (typically a release). Comparing future builds against a baseline detects breaking changes before they ship.
Creating a Baseline¶
# Basic: write to stdout
abicheck dump libfoo.so -H include/foo.h --version 2.0.0
# Write to a specific file
abicheck dump libfoo.so -H include/foo.h --version 2.0.0 -o baseline.json
# Conventionally named (see naming convention below)
abicheck dump libfoo.so -H include/foo.h --version 2.0.0 -o libfoo-2.0.0.abicheck.json
Provenance Metadata¶
Snapshots include provenance metadata that tracks where and when they were created:
abicheck dump libfoo.so -H include/foo.h \
--version 2.0.0 \
--git-tag v2.0.0 \
--build-id "$CI_RUN_ID" \
-o libfoo-2.0.0.abicheck.json
This embeds in the snapshot JSON:
| Field | Source | Example |
|---|---|---|
git_commit |
Auto-detected from git rev-parse HEAD |
abc1234def5678 |
git_tag |
--git-tag flag |
v2.0.0 |
created_at |
Auto-set (ISO 8601 UTC) | 2026-03-24T12:00:00+00:00 |
build_id |
--build-id flag |
gh-actions-1234 |
Use --no-git to skip automatic git commit detection (e.g., in non-git environments).
The .abicheck.json Naming Convention¶
Name baselines <library>-<version>.abicheck.json (via -o):
| Library | Version | Output File |
|---|---|---|
libfoo.so.1 |
2.0.0 |
libfoo-2.0.0.abicheck.json |
bar.dll |
3.1 |
bar-3.1.abicheck.json |
libqux.dylib |
1.0 |
libqux-1.0.abicheck.json |
This convention makes CI scripts predictable: upload with *.abicheck.json, download
with --pattern '*.abicheck.json'. The GitHub Action's abi-baseline input looks for
*.abicheck.json assets on releases.
Storage Patterns¶
abicheck does not mandate where baselines are stored. Choose the pattern that fits your team:
Recipe A: GitHub Releases (Recommended)¶
Best for: open-source libraries, public API contracts.
Release workflow (runs when a release is published):
name: ABI Baseline
on:
release:
types: [published]
jobs:
baseline:
runs-on: ubuntu-latest
permissions:
contents: write # needed for release asset upload
steps:
- uses: actions/checkout@v4
- name: Build
run: make
- name: Dump ABI baseline
uses: abicheck/abicheck@v0.3.0
with:
mode: dump
new-library: build/libfoo.so
new-header: include/foo.h
new-version: ${{ github.ref_name }}
output-file: libfoo-${{ github.ref_name }}.abicheck.json
- name: Upload baseline to release
run: gh release upload ${{ github.ref_name }} libfoo-*.abicheck.json --clobber
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR workflow (compares against latest release baseline):
name: ABI Check
on: pull_request
jobs:
abi:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Build
run: make
- name: ABI compatibility check
uses: abicheck/abicheck@v0.3.0
with:
abi-baseline: latest-release
new-library: build/libfoo.so
new-header: include/foo.h
The abi-baseline: latest-release input automatically downloads the *.abicheck.json
asset from the latest GitHub Release and uses it as the old library.
To pin to a specific release:
- name: ABI compatibility check
uses: abicheck/abicheck@v0.3.0
with:
abi-baseline: v2.0.0
new-library: build/libfoo.so
new-header: include/foo.h
CLI equivalent (requires the gh CLI and GH_TOKEN):
abicheck dump libfoo.so -H include/foo.h \
--version 2.0.0 --git-tag v2.0.0 \
-o libfoo-2.0.0.abicheck.json
gh release upload v2.0.0 libfoo-2.0.0.abicheck.json --clobber
Recipe B: Git-Committed Baselines¶
Best for: small libraries where you want baselines auditable in PR diffs.
# Developer or release CI creates/updates the baseline
abicheck dump libfoo.so -H include/foo.h \
--version 2.0.0 -o abi/libfoo.abicheck.json
git add abi/libfoo.abicheck.json
git commit -m "Update ABI baseline for v2.0.0"
git push
PR workflow:
- name: ABI compatibility check
uses: abicheck/abicheck@v0.3.0
with:
old-library: abi/libfoo.abicheck.json
new-library: build/libfoo.so
new-header: include/foo.h
No download step needed — the baseline file is in the repo.
Recipe C: GitHub Actions Cache¶
Best for: ephemeral, branch-scoped comparisons (e.g., comparing HEAD~1 vs HEAD).
- uses: actions/cache@v4
with:
path: abi-baseline.json
key: abi-baseline-${{ github.event.repository.default_branch }}-${{ github.sha }}
restore-keys: |
abi-baseline-${{ github.event.repository.default_branch }}-
Recipe D: External Artifact Store (S3, Artifactory, GCS)¶
Best for: large binaries, private repos, retention policies.
# Release workflow
- name: Upload baseline to S3
run: aws s3 cp libfoo-2.0.0.abicheck.json s3://my-bucket/abi-baselines/
# PR workflow
- name: Download baseline from S3
run: aws s3 cp s3://my-bucket/abi-baselines/libfoo-2.0.0.abicheck.json baseline.json
- name: ABI check
uses: abicheck/abicheck@v0.3.0
with:
old-library: baseline.json
new-library: build/libfoo.so
new-header: include/foo.h
Baseline Registry¶
For structured baseline management with version/platform addressing and integrity verification, use the built-in baseline registry:
# Push a baseline after a release build
abicheck dump build/libfoo.so -H include/ -o snapshot.json
abicheck baseline push libfoo \
--version 1.0.0 \
--platform linux-x86_64 \
--snapshot snapshot.json
# Pull a baseline by key and compare
abicheck baseline pull libfoo:1.0.0:linux-x86_64 -o baseline.json
abicheck compare baseline.json build/libfoo.so -H include/
# List all baselines
abicheck baseline list
# Delete an old baseline
abicheck baseline delete libfoo:0.9.0:linux-x86_64
Registry commands¶
| Command | Description |
|---|---|
abicheck baseline push <library> |
Store a baseline snapshot |
abicheck baseline pull <spec> |
Retrieve a baseline by key |
abicheck baseline list [prefix] |
List available baselines |
abicheck baseline delete <spec> |
Delete a baseline |
The spec format is library:version:platform[:variant].
Registry options¶
| Flag | Description |
|---|---|
--version |
Version string (e.g., 1.0.0, main) |
--platform |
Target platform (e.g., linux-x86_64, macos-arm64) |
--variant |
Build variant (e.g., ssl-enabled, debug) |
--snapshot |
Path to the ABI snapshot JSON file |
--registry |
Registry directory (default: .abicheck/baselines/) |
--auto-platform |
Auto-detect platform from the snapshot's binary |
--git-commit |
Source commit SHA to record in metadata |
Storage layout¶
Baselines are stored as plain files in a directory tree:
.abicheck/baselines/
├── libfoo/
│ ├── 1.0.0/
│ │ └── linux-x86_64/
│ │ ├── snapshot.json # ABI snapshot
│ │ └── metadata.json # Provenance + checksum
│ └── 2.0.0/
│ └── linux-x86_64/
│ ├── snapshot.json
│ └── metadata.json
└── libbar/
└── 1.0.0/
└── linux-x86_64/
├── snapshot.json
└── metadata.json
Integrity verification¶
Each pushed baseline includes a SHA-256 checksum in metadata.json. On pull,
the checksum is verified and a BaselineIntegrityError is raised if the
snapshot has been modified. Metadata also records:
- abicheck version that produced the snapshot
- Timestamp of creation
- Optional git commit SHA
- Optional build-context hash (when
-pwas used)
Example: CI workflow with the registry¶
jobs:
abi-check:
steps:
- uses: actions/checkout@v4
- name: Build
run: cmake --build build/
- name: Dump ABI snapshot
run: abicheck dump build/libfoo.so -H include/ -o snapshot.json
- name: Pull baseline and compare
run: |
abicheck baseline pull libfoo:latest:linux-x86_64 -o baseline.json \
--registry .abicheck/baselines
abicheck compare baseline.json snapshot.json --format sarif -o abi.sarif
- name: Update baseline (on release tag)
if: startsWith(github.ref, 'refs/tags/v')
run: |
abicheck baseline push libfoo \
--version ${{ github.ref_name }} \
--platform linux-x86_64 \
--snapshot snapshot.json \
--git-commit ${{ github.sha }}
Comparing Against a Baseline¶
Once you have a baseline, comparison is the same regardless of storage:
# JSON snapshot vs new binary
abicheck compare baseline.json build/libfoo.so --new-header include/foo.h
# Two snapshots (no headers or tools needed)
abicheck compare old-baseline.json new-baseline.json
Snapshots are self-contained — they include all type, function, variable, and enum information. Comparing two snapshots requires no headers, compilers, or debug info.
scan --baseline against a native library¶
abicheck scan takes a single -H (built for the new binary). When the
--baseline is a native library (not a snapshot) and its public headers
differ from the new version, parse the old side with its own headers:
abicheck scan --binary new/libfoo.so -H new/include \
--baseline old/libfoo.so --baseline-header old/include
--baseline-header/--baseline-include— the old side's public headers and include roots. Without them, a native baseline is parsed with the new-H(correct only when the headers didn't change), andscanprints a warning.- A JSON-snapshot baseline already has its headers baked in, so these flags are unnecessary there. Prefer a pre-dumped snapshot baseline when you can — it's unambiguous and needs no toolchain at compare time.