feat: publish libcpex_ffi.a as signed release artifacts#59
Merged
Conversation
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
373c0e9 to
87399f3
Compare
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
Signed-off-by: Frederico Araujo <frederico.araujo@ibm.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Publish
libcpex_ffi.aas signed GitHub Release artifacts on everysemver tag push, so downstream Go (and future language) bindings can
link against a pre-built static library without installing a Rust
toolchain. Adds a versioned FFI ABI contract so cross-version
mismatches surface as a loud
init()panic instead of silent UB.Why
Today every consumer of the CPEX Go binding has to clone this repo
and run
cargo build --releasethemselves. That pushes the Rusttoolchain into every downstream CI (kagenti-extensions, anyone
embedding CPEX) and turns the FFI ABI into a runtime surprise
instead of a versioned contract. Publishing the
.aas a signedrelease artifact removes Rust from downstream builds and turns ABI
mismatches into a clean version-resolution error.
What ships
release-ffi.yamlworkflow on semver-strict tag push.scripts/download-ffi-artifact.shfor theverify-and-unpack recipe.
FFI_ABI_VERSIONinteger baked into the crate and checked atGo binding init.
crates/cpex-ffi/RELEASE.md.Changes
crates/cpex-ffi/src/lib.rspub const FFI_ABI_VERSION: u32 = 1;andextern "C" fn cpex_ffi_abi_version(). Section docs define what counts as an ABI-breaking change.go/cpex/abi.go(new)expectedFFIABIVersion = 1+init()panic-on-mismatch. Self-contained cgo preamble (cgo declarations don't cross files in this package).scripts/release/build-artifact.sh(new)libcpex_ffi.afor a givenTARGETRust triple, stages tarball withVERSION/FFI_ABI/LICENSE, emits sha256 companion. Maps Rust triples to our tuple naming (linux-amd64-gnu, etc.).scripts/release/sign-artifact.sh(new)DIST_DIR.scripts/download-ffi-artifact.sh(new)uname(with musl/gnu probe), downloads, verifies sha256, verifies cosign signature against the canonical workflow identity, unpacks. Idempotent on re-run..github/workflows/release-ffi.yaml(new)v[0-9]+.[0-9]+.[0-9]++v[0-9]+.[0-9]+.[0-9]+-*(semver-strict; excludes legacy / vendor tags). 5-tuple matrix build, separate sign-and-release job. Prereleases auto-flagged. Idempotentgh release create/upload --clobber. Allgithub.ref_nameinterpolations flow throughenv:blocks.crates/cpex-ffi/RELEASE.md(new)CHANGELOG.mdTarget matrix
linux-amd64-gnux86_64-unknown-linux-gnuubuntu-latestlinux-arm64-gnuaarch64-unknown-linux-gnuubuntu-22.04-armlinux-amd64-muslx86_64-unknown-linux-muslubuntu-latestlinux-arm64-muslaarch64-unknown-linux-muslubuntu-22.04-armdarwin-arm64aarch64-apple-darwinmacos-14darwin-amd64and Windows deferred; matrix is easy to extend later.Artifact bundle
For each release tag
vX.Y.Zand tuple:Plus one aggregate
SHA256SUMS+.sig+.crtfor the whole release.Validation
A dry-run tag
v0.0.0-ffi.test.1was pushed against this branchbefore this PR opened. Results (release + tag have since been
cleaned up):
branch of the tag matcher.
assets (5 tarballs × 4 files + 3-file aggregate SHA256SUMS bundle)
flagged as prerelease.
VERSION.git_shamatched the tagged commit;FFI_ABI=1;_cpex_ffi_abi_versionsymbol exported in the published.a.scripts/download-ffi-artifact.shend-to-end against thepublished release: SHA256 verified, tarball unpacked, idempotency
short-circuit fires on re-run.
identity regex matches the canonical workflow path; OIDC issuer
matches GitHub Actions).
Local consequences for the existing in-tree builds:
cargo check -p cpex-ffi,make rust-build-release, andgo test ./go/cpex/...all pass — the Go binding's new
init()ABI check fires at everytest-binary load and is non-fatal because
expectedFFIABIVersionmatches the just-built library's
FFI_ABI_VERSION.Notes for reviewers
lib.rsand thesame in
RELEASE.md. Adding a newRC_*code at the end is nota break; renaming / removing / re-signing any extern is. Bump
flows through to
go/cpex/abi.go'sexpectedFFIABIVersioninthe same PR.
the workflow that produces them are one contract; splitting them
leaves an awkward middle state where scripts exist but no
published artifact exercises them. The dry-run validation gate
(above) is the right test, not a second PR.
v*would matchvendor-bump tags, accidental
v1/v-test, etc. The twopatterns
v[0-9]+.[0-9]+.[0-9]++v[0-9]+.[0-9]+.[0-9]+-*only fire on
vMAJOR.MINOR.PATCH(optionally prerelease) andcleanly exclude this repo's pre-
vlegacy tags (0.1.0,plugins.dev1).${{ github.ref_name }}interpolation that reaches a
run:block flows through anenv:variable and is referenced as
"$VAR"(quoted). No directexpression substitution into shell.
about
actions/checkout@v4,actions/upload-artifact@v4, andactions/download-artifact@v4still running on Node 20 — GitHubwill auto-force Node 24 on these actions starting June 16, 2026.
No code change needed.
rekor.sigstore.dev(some corporate egress filters, CiscoUmbrella, etc.) will fail the default cosign verify because it
queries the transparency log online. The signature itself is
fine; the user would need
--insecure-ignore-tlogto verifywithout Rekor. A follow-up could have the signing job emit a
cosign bundle (
--bundle file.bundle) that embeds theRekor inclusion proof inline, removing the online dependency.
Out of scope here; worth a separate small PR if the egress
pattern shows up in practice.
Out of scope (deferred to future PRs)
cbindgen. The Go binding embeds Cdeclarations inline in
ffi.go; no other language binding needsheaders today.
ghcr.io/contextforge-org/cpex-ffi. TheGitHub Releases attachments are the source of truth; mirror is
easy to add later.
darwin-amd64and Windows targets.cargo --lockedgives us.