ci : add API/ABI check to make-release workflow [no ci] (#28947)

* ci : add API/ABI check to make-release workflow [no ci]

This commit adds an API/ABI compatibility check to the make-release
workflow.

The motivation for this to allow us to detect any potential breaking
changes in API/ABI compatibility between releases and fail the the
release if there are any.

The workflow can be triggered manually as before and this check can be
skipped if needed as it does take some time which might be useful when
doing a dry-run and not specifically interested in the API/ABI check.

By default this will check the current release against the latest
release, but this can also be configured in the workflow, or in the
script run on the command line, to check a different tag.

* add check for minor version bumps [no ci]

This commit also changes the build type to be RelWithDebInfo so that the
reported information is more useful.
This commit is contained in:
Daniel Bevenius
2026-09-17 09:55:16 +02:00
committed by GitHub
parent 7f6f0c2a9d
commit 87f9c82f2d
4 changed files with 185 additions and 3 deletions
+16
View File
@@ -13,6 +13,16 @@ on:
required: true
type: boolean
default: true
skip_apiabi_check:
description: 'Skip API/ABI compatibility check'
required: false
type: boolean
default: false
apiabi_compare_tag:
description: 'Tag to compare against for API/ABI check (default: latest release)'
required: false
type: string
default: ''
env:
GH_TOKEN: ${{ github.token }}
@@ -33,12 +43,18 @@ jobs:
ref: ${{ inputs.commit != '' && inputs.commit || github.ref_name }}
fetch-depth: 0
- name: Install API/ABI check tools
if: ${{ github.event.inputs.skip_apiabi_check != 'true' }}
run: sudo apt-get install -y abi-compliance-checker abigail-tools
- name: Run release checks
id: checks
run: bash scripts/make-release-checks.sh ${{ github.event.inputs.dry_run == 'true' && '--dry-run' || '' }}
env:
GITHUB_REPOSITORY: ${{ github.repository }}
RELEASE_BRANCH: ${{ github.ref_name }}
SKIP_APIABI_CHECK: ${{ github.event.inputs.skip_apiabi_check }}
APIABI_COMPARE_TAG: ${{ github.event.inputs.apiabi_compare_tag }}
- name: Create release tag
if: ${{ github.event.inputs.dry_run == 'false' }}
+21 -3
View File
@@ -55,7 +55,9 @@ fi
# Some generic functions
usage() {
echo "Usage: $0 [ --include-path <dir> ] --generate <build-dir> libXXX [ libYYY ... ]" >&2
echo " $0 --check <old-build-dir> <new-build-dir>" >&2
echo " $0 [ --strict ] --check <old-build-dir> <new-build-dir>" >&2
echo "" >&2
echo " --strict: fail on any API/ABI change, including backwards-compatible additions" >&2
}
get_cmake_project_name() {
@@ -72,6 +74,7 @@ get_cmake_version() {
# Option parsing and validation
DO_GEN=0
DO_CHECK=0
STRICT=0
BUILD_DIR=
BUILD_DIR_NEW=
INCLUDE_PATHS=
@@ -119,6 +122,10 @@ while [ "$#" -gt 0 ]; do
INCLUDE_PATHS="$INCLUDE_PATHS $2"
shift 2
;;
--strict)
STRICT=1
shift
;;
-h | --help)
usage
exit 1
@@ -258,13 +265,24 @@ elif [ "$DO_CHECK" -eq 1 ]; then
abidiff "$xml_file" "$xml_file_new"
res=$?
[ "$((res & 8))" -ne 0 ] && ABI_RESULT=1
# check bit 2 for compatible ABI changes (like new symbols) and if
# STRICT is set then handle this as an error.
[ "$STRICT" -eq 1 ] && [ "$((res & 4))" -ne 0 ] && ABI_RESULT=1
done
if [ "$API_RESULT" -gt 0 ]; then
echo "ERROR: API changed with possible backwards-compatibility problems." >&2
if [ "$STRICT" -eq 1 ]; then
echo "ERROR: API changed — a minor version bump is required." >&2
else
echo "ERROR: API changed with backwards-incompatible problems — a major version bump is required." >&2
fi
fi
if [ "$ABI_RESULT" -gt 0 ]; then
echo "ERROR: ABI changed with possible backwards-compatibility problems." >&2
if [ "$STRICT" -eq 1 ]; then
echo "ERROR: ABI changed — a minor version bump is required." >&2
else
echo "ERROR: ABI changed with backwards-incompatible problems — a major version bump is required." >&2
fi
fi
if [ "$((API_RESULT + ABI_RESULT))" -gt 0 ]; then
exit 1
+131
View File
@@ -0,0 +1,131 @@
#!/bin/bash
# Check API/ABI compatibility between the previous release tag and current HEAD.
#
# Finds the most recent vX.Y.Z tag, checks it out in a temporary git worktree,
# builds both versions with shared libs enabled, and uses check-apiabi-compat.sh
# to compare the results.
#
# Exit codes:
# 0: compatible, or check was skipped
# 1: backwards-incompatible changes found, or build failed
#
# Options:
# --tag <version>: compare against this tag instead of the latest release
#
# Environment:
# SKIP_APIABI_CHECK: set to 1 or true to skip
# APIABI_COMPARE_TAG: equivalent to --tag (used by CI)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
usage() {
echo "Usage: $0 [--tag <version>]" >&2
echo " --tag <version> Compare against this release tag (default: latest)" >&2
}
COMPARE_TAG="${APIABI_COMPARE_TAG:-}"
while [[ "$#" -gt 0 ]]; do
case "$1" in
--tag)
if [[ -z "${2:-}" ]]; then usage; exit 1; fi
COMPARE_TAG="$2"
shift 2
;;
--tag=*)
COMPARE_TAG="${1#*=}"
shift
;;
-h | --help)
usage; exit 0
;;
*)
usage; exit 1
;;
esac
done
if [[ "${SKIP_APIABI_CHECK:-}" == "1" || "${SKIP_APIABI_CHECK:-}" == "true" ]]; then
echo "SKIP_APIABI_CHECK is set - skipping API/ABI compatibility check"
exit 0
fi
if ! command -v abi-compliance-checker >/dev/null 2>&1 || ! command -v abidw >/dev/null 2>&1; then
echo "Warning: abi-compliance-checker or abigail-tools not installed - skipping API/ABI check"
exit 0
fi
discover_libs() {
local build_dir="$1"
local libs=()
for dir in "$build_dir/src" "$build_dir/bin"; do
[[ -d "$dir" ]] || continue
for f in "$dir"/lib*.so; do
[[ -f "$f" ]] && libs+=("$(basename "$f" .so)")
done
done
echo "${libs[@]}"
}
if [[ -n "${COMPARE_TAG}" ]]; then
PREV_TAG="${COMPARE_TAG}"
if ! git -C "$REPO_ROOT" rev-parse --verify "${PREV_TAG}^{}" >/dev/null 2>&1; then
echo "Error: tag '${PREV_TAG}' not found in repository." >&2
exit 1
fi
else
PREV_TAG=$(git -C "$REPO_ROOT" tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1 || true)
if [[ -z "${PREV_TAG}" ]]; then
echo "Warning: no previous release tag found - skipping API/ABI check"
exit 0
fi
fi
OLD_VERSION="${PREV_TAG#v}"
OLD_MAJOR="${OLD_VERSION%%.*}"
OLD_MINOR="${OLD_VERSION#*.}"; OLD_MINOR="${OLD_MINOR%%.*}"
NEW_MAJOR=$(grep "set(LLAMA_VERSION_MAJOR" "$REPO_ROOT/CMakeLists.txt" | sed 's/.*MAJOR \([0-9]*\).*/\1/')
NEW_MINOR=$(grep "set(LLAMA_VERSION_MINOR" "$REPO_ROOT/CMakeLists.txt" | sed 's/.*MINOR \([0-9]*\).*/\1/')
if [[ "$NEW_MAJOR" -gt "$OLD_MAJOR" ]]; then
echo "Major version increment ($OLD_MAJOR -> $NEW_MAJOR): API/ABI breaking changes are expected, skipping compatibility check."
exit 0
fi
CHECK_FLAGS=()
if [[ "$NEW_MINOR" -eq "$OLD_MINOR" ]]; then
echo "Patch version bump detected: checking for any API/ABI changes (a minor bump is required if any are found)..."
CHECK_FLAGS+=(--strict)
else
echo "Minor version bump detected: checking for backwards-incompatible API/ABI changes..."
fi
echo "Checking API/ABI compatibility against ${PREV_TAG}..."
WORKTREE_DIR=$(mktemp -d)
BUILD_OLD=$(mktemp -d)
BUILD_NEW=$(mktemp -d)
cleanup() {
git -C "$REPO_ROOT" worktree remove --force "$WORKTREE_DIR" 2>/dev/null || true
rm -rf "$WORKTREE_DIR" "$BUILD_OLD" "$BUILD_NEW"
}
trap cleanup EXIT
git -C "$REPO_ROOT" worktree add "$WORKTREE_DIR" "$PREV_TAG"
cmake -S "$WORKTREE_DIR" -B "$BUILD_OLD" -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build "$BUILD_OLD" --parallel "$(nproc)"
OLD_LIBS=($(discover_libs "$BUILD_OLD"))
echo "Libraries found in old build: ${OLD_LIBS[*]}"
cmake -S "$REPO_ROOT" -B "$BUILD_NEW" -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake --build "$BUILD_NEW" --parallel "$(nproc)"
NEW_LIBS=($(discover_libs "$BUILD_NEW"))
echo "Libraries found in new build: ${NEW_LIBS[*]}"
(cd "$WORKTREE_DIR" && "$SCRIPT_DIR/check-apiabi-compat.sh" --include-path ggml/include --generate "$BUILD_OLD" "${OLD_LIBS[@]}")
(cd "$REPO_ROOT" && "$SCRIPT_DIR/check-apiabi-compat.sh" --include-path ggml/include --generate "$BUILD_NEW" "${NEW_LIBS[@]}")
(cd "$REPO_ROOT" && "$SCRIPT_DIR/check-apiabi-compat.sh" "${CHECK_FLAGS[@]}" --check "$BUILD_OLD" "$BUILD_NEW")
+17
View File
@@ -165,6 +165,23 @@ else
fi
fi
echo "Checking API/ABI compatibility..."
set +e
bash "$SCRIPT_DIR/check-release-apiabi.sh"
APIABI_RESULT=$?
set -e
if [[ $APIABI_RESULT -ne 0 ]]; then
if [[ "$DRY_RUN" == "true" ]]; then
echo "Warning: API/ABI check found backwards-incompatible changes (dry run, continuing)."
CHECKS_PASSED=false
else
echo "Error: API/ABI check found backwards-incompatible changes."
exit 1
fi
else
echo "API/ABI compatibility check passed - OK"
fi
if [[ -n "${GITHUB_OUTPUT:-}" ]]; then
echo "checks_passed=${CHECKS_PASSED}" >> "$GITHUB_OUTPUT"
fi