Skip to content

Commit 3f1566c

Browse files
committed
merge: merging prebuilt cpexlib_ffi.a into APL branch.
2 parents 5f38887 + c83a655 commit 3f1566c

16 files changed

Lines changed: 1243 additions & 21 deletions

File tree

.github/workflows/release-ffi.yaml

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
# ===============================================================
2+
# Release FFI - Build, sign, and publish libcpex_ffi.a artifacts
3+
# ===============================================================
4+
#
5+
# Triggered by semver-strict tag pushes. Matrix-builds the FFI
6+
# static library for the supported target tuples, packages each into
7+
# a tarball with VERSION / FFI_ABI / LICENSE metadata, signs every
8+
# tarball + the aggregate SHA256SUMS with cosign keyless (Sigstore),
9+
# and attaches everything to the GitHub Release for the tag.
10+
#
11+
# See crates/cpex-ffi/RELEASE.md for the artifact schema and the
12+
# consumer-side verify-and-unpack recipe.
13+
14+
name: Release FFI
15+
16+
on:
17+
push:
18+
tags:
19+
# Semver-strict. Two patterns so vMAJOR.MINOR.PATCH (release)
20+
# and vMAJOR.MINOR.PATCH-<prerelease> (rc / beta / ffi.test)
21+
# both fire, while loose `v*` matches (vendor-bump, v1, v-foo)
22+
# and the legacy non-prefixed tags (0.1.0, plugins.dev1) do not.
23+
# Dry-run tags like v0.0.0-ffi.test.1 deliberately hit the
24+
# prerelease branch.
25+
- 'v[0-9]+.[0-9]+.[0-9]+'
26+
- 'v[0-9]+.[0-9]+.[0-9]+-*'
27+
workflow_dispatch:
28+
29+
# id-token: write is what unlocks Sigstore keyless signing (Fulcio
30+
# reads the GHA OIDC token to issue the short-lived signing cert).
31+
# contents: write is needed to create / upload to the GitHub Release.
32+
permissions:
33+
contents: write
34+
id-token: write
35+
36+
# Prevent concurrent runs on the same tag from racing the release
37+
# creation. Tag pushes are one-shot, so this is belt-and-suspenders.
38+
concurrency:
39+
group: release-ffi-${{ github.ref }}
40+
cancel-in-progress: false
41+
42+
jobs:
43+
build:
44+
name: Build ${{ matrix.target }}
45+
runs-on: ${{ matrix.runner }}
46+
strategy:
47+
fail-fast: false # one tuple failing should not cancel the others
48+
matrix:
49+
include:
50+
- target: x86_64-unknown-linux-gnu
51+
runner: ubuntu-latest
52+
- target: aarch64-unknown-linux-gnu
53+
runner: ubuntu-22.04-arm
54+
- target: x86_64-unknown-linux-musl
55+
runner: ubuntu-latest
56+
- target: aarch64-unknown-linux-musl
57+
runner: ubuntu-22.04-arm
58+
- target: aarch64-apple-darwin
59+
runner: macos-14
60+
61+
steps:
62+
- name: Checkout
63+
uses: actions/checkout@v4
64+
65+
- name: Install Rust toolchain
66+
# dtolnay/rust-toolchain is the de-facto rustup action.
67+
# `stable` picks the latest stable; pin if we need a floor.
68+
uses: dtolnay/rust-toolchain@stable
69+
with:
70+
targets: ${{ matrix.target }}
71+
72+
- name: Cache cargo build
73+
uses: Swatinem/rust-cache@v2
74+
with:
75+
# Share cache across tags (the key includes the target +
76+
# Cargo.lock hash by default). Significant speedup on
77+
# subsequent releases of the same target.
78+
key: ${{ matrix.target }}
79+
80+
- name: Install musl toolchain
81+
# ring (via jsonwebtoken/rustls/quinn) compiles C + asm through
82+
# cc-rs, which for a *-unknown-linux-musl target shells out to
83+
# <arch>-linux-musl-gcc. The runners ship only the glibc gcc, so
84+
# we install musl-gcc here. The matrix runs each musl target on
85+
# its native-arch runner, so musl-gcc targets the host arch and
86+
# the CC_/LINKER env vars below redirect cc-rs and the linker to
87+
# it. Scoped to the musl legs; gnu/darwin use their default cc.
88+
if: contains(matrix.target, 'musl')
89+
run: sudo apt-get update && sudo apt-get install -y musl-tools musl-dev
90+
91+
- name: Build artifact
92+
env:
93+
TARGET: ${{ matrix.target }}
94+
# VERSION drops the leading "refs/tags/" so the tarball
95+
# name matches the tag verbatim.
96+
VERSION: ${{ github.ref_name }}
97+
DIST_DIR: dist
98+
# Point cc-rs and the linker at musl-gcc for the musl targets.
99+
# No-ops for gnu/darwin (those triples don't match these keys).
100+
CC_x86_64_unknown_linux_musl: musl-gcc
101+
CC_aarch64_unknown_linux_musl: musl-gcc
102+
CARGO_TARGET_X86_64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc
103+
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER: musl-gcc
104+
run: bash scripts/release/build-artifact.sh
105+
106+
- name: Upload tarball + sha256
107+
uses: actions/upload-artifact@v4
108+
with:
109+
# Unique per-target name so the download step in
110+
# sign-and-release can merge them all into one dist/.
111+
name: cpex-ffi-${{ github.ref_name }}-${{ matrix.target }}
112+
path: dist/cpex-ffi-*
113+
if-no-files-found: error
114+
retention-days: 7
115+
116+
sign-and-release:
117+
name: Sign and publish release
118+
needs: [build]
119+
runs-on: ubuntu-latest
120+
steps:
121+
- name: Checkout
122+
uses: actions/checkout@v4
123+
124+
- name: Download all matrix artifacts
125+
uses: actions/download-artifact@v4
126+
with:
127+
path: dist
128+
# merge-multiple flattens the per-target subdirs into one
129+
# dist/ so the sign script and gh release upload see a
130+
# flat layout.
131+
merge-multiple: true
132+
133+
- name: Generate aggregate SHA256SUMS
134+
env:
135+
VERSION: ${{ github.ref_name }}
136+
run: |
137+
set -euo pipefail
138+
cd dist
139+
# Concat all individual .sha256 files into one signed
140+
# integrity manifest. The per-tarball .sha256 files stay
141+
# as convenience companions, but the SHA256SUMS file is
142+
# what auditors care about.
143+
: > "cpex-ffi-${VERSION}-SHA256SUMS"
144+
for f in cpex-ffi-*.tar.gz; do
145+
if command -v sha256sum >/dev/null; then
146+
sha256sum "$f" >> "cpex-ffi-${VERSION}-SHA256SUMS"
147+
else
148+
shasum -a 256 "$f" >> "cpex-ffi-${VERSION}-SHA256SUMS"
149+
fi
150+
done
151+
echo "--- SHA256SUMS ---"
152+
cat "cpex-ffi-${VERSION}-SHA256SUMS"
153+
154+
- name: Install cosign
155+
uses: sigstore/cosign-installer@v3
156+
157+
- name: Sign artifacts
158+
env:
159+
DIST_DIR: dist
160+
run: bash scripts/release/sign-artifact.sh
161+
162+
- name: Create or update GitHub Release
163+
env:
164+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
165+
TAG: ${{ github.ref_name }}
166+
run: |
167+
set -euo pipefail
168+
169+
# Auto-detect prerelease by suffix so dry-run tags
170+
# (v0.0.0-ffi.test.1) land as prereleases and don't surface
171+
# as "latest" on the repo's Releases page.
172+
PRERELEASE_FLAG=""
173+
if [[ "$TAG" == *-* ]]; then
174+
PRERELEASE_FLAG="--prerelease"
175+
fi
176+
177+
# Idempotent: if the release exists, upload --clobber the
178+
# new files; if it doesn't, create it with the tarballs +
179+
# SHA256SUMS + sigs in one shot.
180+
if gh release view "$TAG" >/dev/null 2>&1; then
181+
echo "Release $TAG exists; uploading artifacts with --clobber"
182+
gh release upload "$TAG" dist/cpex-ffi-* --clobber
183+
else
184+
echo "Creating release $TAG"
185+
gh release create "$TAG" \
186+
--title "$TAG" \
187+
--notes "Automated FFI artifact release. See crates/cpex-ffi/RELEASE.md for the schema and verify-and-consume recipe." \
188+
$PRERELEASE_FLAG \
189+
dist/cpex-ffi-*
190+
fi

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,36 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
1515
1616
## [Unreleased]
1717

18+
### Added
19+
20+
- APL (Attribute Policy Language) governance is now bundled into
21+
`libcpex_ffi.a`. New `cpex_apl_install` extern C entry point registers
22+
the standard APL plugin/PDP factories (`validator/pii-scan`,
23+
`audit/logger`, `identity/jwt`, `delegator/oauth`, `cedar-direct`) and
24+
installs the APL config visitor on a manager. Call it after
25+
`cpex_manager_new_default` and before `cpex_load_config`. Go hosts use
26+
`PluginManager.EnableAPL()`. The optional `cedarling` cargo feature adds
27+
the Cedarling-backed identity + PDP seams (off by default; the released
28+
`.a` stays lean).
29+
- Publish `libcpex_ffi.a` as signed GitHub Release artifacts on
30+
every semver tag push (`linux-amd64-gnu`, `linux-arm64-gnu`,
31+
`linux-amd64-musl`, `linux-arm64-musl`, `darwin-arm64`). Cosign
32+
keyless signatures + SHA256 checksums; see
33+
`crates/cpex-ffi/RELEASE.md` for the schema and the verify-and-
34+
consume recipe.
35+
- FFI ABI versioning: `cpex_ffi_abi_version()` extern C accessor
36+
exposes `FFI_ABI_VERSION`. The Go binding checks this in `init()`
37+
and panics on mismatch. Other language bindings must replicate the
38+
check.
39+
40+
### Changed
41+
42+
- FFI `FFI_ABI_VERSION` bumped `1 → 2`: added the `cpex_apl_install`
43+
extern C function and changed `cpex_load_config` to run registered
44+
config visitors (it now calls `load_config_yaml` internally so `apl:`
45+
blocks are walked). The Go binding's `expectedFFIABIVersion` is bumped
46+
in lockstep.
47+
1848
## [0.1.0] - 2026-05-05
1949

2050
### Added

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/cpex-ffi/Cargo.toml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,31 @@ crate-type = ["lib", "cdylib", "staticlib"]
2020

2121
[dependencies]
2222
cpex-core = { path = "../cpex-core" }
23+
# APL governance layer — bundled so Go/Python hosts can enable APL
24+
# policies, route handlers, and the standard plugin/PDP factories via
25+
# the `cpex_apl_install` FFI entry point. Symbols survive in the
26+
# staticlib because that entry point references each factory.
27+
apl-cpex = { path = "../apl-cpex" }
28+
apl-pii-scanner = { path = "../apl-pii-scanner" }
29+
apl-audit-logger = { path = "../apl-audit-logger" }
30+
apl-identity-jwt = { path = "../apl-identity-jwt" }
31+
apl-delegator-oauth = { path = "../apl-delegator-oauth" }
32+
apl-pdp-cedar-direct = { path = "../apl-pdp-cedar-direct" }
33+
# Heavy (~200 transitive deps via the Cedarling git dep); kept out of the
34+
# default `.a` and behind the `cedarling` feature.
35+
apl-cedarling = { path = "../apl-cedarling", optional = true }
2336
tokio = { workspace = true }
2437
serde = { workspace = true }
2538
serde_json = { workspace = true }
2639
rmp-serde = { workspace = true }
2740
serde_bytes = { workspace = true }
2841
tracing = { workspace = true }
2942

43+
[features]
44+
default = []
45+
# Opt-in Cedarling-backed identity + PDP. Build with
46+
# `cargo build -p cpex-ffi --features cedarling`.
47+
cedarling = ["dep:apl-cedarling"]
48+
3049
[dev-dependencies]
3150
async-trait = { workspace = true }

0 commit comments

Comments
 (0)