Skip to content

Commit 3e66ee0

Browse files
Merge branch 'main' into feat/ocaml-binding-standup
2 parents 6d5690b + 4404e7a commit 3e66ee0

23 files changed

Lines changed: 562 additions & 155 deletions

.claude/settings.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"permissions": {
3+
"allow": [
4+
"Bash(cargo check)",
5+
"Bash(cargo check *)",
6+
"Bash(gh project item-list *)",
7+
"Bash(gh project list)",
8+
"Bash(gh project list *)",
9+
"Bash(git grep *)"
10+
]
11+
}
12+
}

.clusterfuzzlite/Dockerfile

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,27 @@
44
# ClusterFuzzLite fuzzing container for proven.
55
#
66
# Base: ClusterFuzzLite's rust image (libfuzzer + cargo-fuzz pre-installed)
7-
# Adds: Zig + Idris2 so build.sh can compile libproven.so prior to
8-
# building the fuzz targets.
7+
# Adds: Zig so build.sh can compile the standalone libproven.so fixture
8+
# prior to building the supported fuzz targets.
99

10-
FROM gcr.io/oss-fuzz-base/base-builder-rust:v1@sha256:5c2cdc8a9d8b6ad0bf7e9f44fdcd0d7cd7a8b6eca5e8d3c5a89e3e5d8e1c7a3f
10+
FROM gcr.io/oss-fuzz-base/base-builder-rust:v1@sha256:8a9f7d1ee933da04fc7b616c51d3fb99009692f6aa93ca9e972a6a39730e14f5
1111

12-
# Idris2 + Zig dependencies. ChezScheme is the Idris2 bootstrap target;
13-
# gmp is the bignum dependency.
12+
# Zig download dependencies.
1413
RUN apt-get update && apt-get install -y --no-install-recommends \
15-
chezscheme \
16-
libgmp-dev \
1714
curl \
1815
ca-certificates \
19-
git \
20-
build-essential \
16+
xz-utils \
2117
&& rm -rf /var/lib/apt/lists/*
2218

23-
# Pin Zig 0.13.0 (matches proven's .github/workflows/zig-ffi.yml).
24-
ARG ZIG_VERSION=0.13.0
25-
RUN curl -sL "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-x86_64-${ZIG_VERSION}.tar.xz" \
26-
| tar -xJ -C /opt \
27-
&& ln -s "/opt/zig-linux-x86_64-${ZIG_VERSION}/zig" /usr/local/bin/zig
28-
29-
# Build Idris2 0.8.0 from source (matches proven's idris2-ci.yml).
30-
ARG IDRIS2_VERSION=0.8.0
31-
RUN git clone --depth 1 --branch "v${IDRIS2_VERSION}" \
32-
https://github.com/idris-lang/Idris2.git /tmp/idris2 \
33-
&& cd /tmp/idris2 \
34-
&& make bootstrap SCHEME=chezscheme \
35-
&& make install \
36-
&& rm -rf /tmp/idris2
37-
38-
ENV PATH="/root/.idris2/bin:${PATH}"
19+
# Pin Zig 0.15.2 (matches ffi/zig/build.zig.zon's minimum).
20+
ARG ZIG_VERSION=0.15.2
21+
ARG ZIG_TARBALL="https://ziglang.org/download/${ZIG_VERSION}/zig-x86_64-linux-${ZIG_VERSION}.tar.xz"
22+
ARG ZIG_SHA256="02aa270f183da276e5b5920b1dac44a63f1a49e55050ebde3aecc9eb82f93239"
23+
RUN curl -fsSL "$ZIG_TARBALL" -o /tmp/zig.tar.xz \
24+
&& echo "$ZIG_SHA256 /tmp/zig.tar.xz" | sha256sum -c - \
25+
&& tar -xJ -C /opt -f /tmp/zig.tar.xz \
26+
&& ln -s "/opt/zig-x86_64-linux-${ZIG_VERSION}/zig" /usr/local/bin/zig \
27+
&& rm /tmp/zig.tar.xz
3928

4029
# Project source is mounted at $SRC/proven by ClusterFuzzLite.
4130
WORKDIR $SRC/proven

.clusterfuzzlite/build.sh

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
# SPDX-License-Identifier: MPL-2.0
33
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
44
#
5-
# ClusterFuzzLite build script. Builds libproven via Zig (which compiles
6-
# Idris2 RefC output) and then builds the cargo-fuzz targets that link
7-
# against it.
5+
# ClusterFuzzLite build script. Builds the standalone Zig FFI surface as
6+
# libproven.so, then builds the cargo-fuzz targets that are backed by that
7+
# surface.
88
#
99
# Runs inside the ClusterFuzzLite container (see Dockerfile). Outputs
1010
# the fuzz target binaries to $OUT, the path the runner expects.
@@ -17,43 +17,46 @@
1717
set -euo pipefail
1818

1919
echo "=== ClusterFuzzLite build: proven ==="
20+
mkdir -p "$OUT"
2021

21-
# Build libproven via the Zig FFI bridge. The container has Idris2 + Zig
22-
# baked in; this step compiles Idris2 → C → libproven.so.
22+
# Build the standalone Zig FFI fixture as libproven.so. The full Idris RefC
23+
# bridge is exercised by ffi-full-integration.yml when its private dependency
24+
# credential is configured; this PR fuzz gate must stay self-contained.
2325
if [[ -f ffi/zig/build.zig ]]; then
2426
pushd ffi/zig >/dev/null
25-
zig build || {
26-
echo "WARN: Zig FFI build failed; fuzz targets will not link." >&2
27-
echo " Investigate libproven build before re-running." >&2
27+
mkdir -p zig-out/lib
28+
zig build-lib -static -O ReleaseSafe -fno-stack-check -fPIC --name proven \
29+
-femit-bin=zig-out/lib/libproven.a src/main.zig -lc || {
30+
echo "WARN: standalone Zig FFI build failed; fuzz targets will not link." >&2
2831
exit 1
2932
}
33+
3034
popd >/dev/null
3135
fi
3236

3337
# Make libproven discoverable to the cargo-fuzz linker.
3438
export PROVEN_LIB_DIR="$PWD/ffi/zig/zig-out/lib"
3539
export LD_LIBRARY_PATH="$PROVEN_LIB_DIR:${LD_LIBRARY_PATH:-}"
40+
export CXXFLAGS="${CXXFLAGS:+$CXXFLAGS }-stdlib=libstdc++"
3641

3742
# Build cargo-fuzz targets and stage them at $OUT (ClusterFuzzLite
3843
# convention; runner reads from there).
3944
pushd bindings/rust/fuzz >/dev/null
40-
cargo fuzz build -O --debug-assertions
45+
cargo +nightly fuzz build safe_path_has_traversal -O --debug-assertions
4146

4247
# Copy each compiled fuzzer + libproven.so into $OUT.
4348
TARGETS_DIR="target/x86_64-unknown-linux-gnu/release"
44-
for fuzzer in safe_url_parse safe_email_is_valid safe_json_is_valid safe_path_has_traversal; do
49+
for fuzzer in safe_path_has_traversal; do
4550
if [[ -f "$TARGETS_DIR/$fuzzer" ]]; then
4651
cp "$TARGETS_DIR/$fuzzer" "$OUT/"
4752
else
4853
echo "WARN: missing fuzz binary $fuzzer (build failed)" >&2
4954
fi
5055
done
5156

52-
# Bundle libproven.so so the runner can find it at exec time.
53-
mkdir -p "$OUT/lib"
54-
if [[ -f "$PROVEN_LIB_DIR/libproven.so" ]]; then
55-
cp "$PROVEN_LIB_DIR/libproven.so" "$OUT/lib/"
56-
fi
57+
# Bundle libproven.a so the fuzzer build can find it (for static linking).
58+
# Note: ClusterFuzzLite binaries should be self-contained; static linking
59+
# is preferred to avoid runtime library loading issues.
5760
popd >/dev/null
5861

5962
echo "=== Build complete. Staged to $OUT. ==="

.github/copilot/coding-agent.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
mcp_servers:
2+
boj-server:
3+
command: npx
4+
args: ["-y", "@hyperpolymath/boj-server@latest"]
5+
env:
6+
BOJ_URL: http://localhost:7700

.github/workflows/boj-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
BRANCH: ${{ github.ref_name }}
3030
run: |
3131
# Send a secure trigger to boj-server to build this repository.
32-
# Payload values are passed via env vars rather than ${{ }}
32+
# Payload values are passed via env vars rather than workflow expressions
3333
# interpolation to avoid shell-injection by branch name.
3434
payload=$(jq -nc \
3535
--arg repo "$REPO" \

.github/workflows/clusterfuzzlite.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ concurrency:
2929

3030
jobs:
3131
pr-fuzzing:
32-
if: github.event_name == 'pull_request' && hashFiles('bindings/rust/fuzz/Cargo.toml') != ''
32+
if: github.event_name == 'pull_request'
3333
runs-on: ubuntu-latest
3434
timeout-minutes: 30
3535
steps:

.github/workflows/dogfood-gate.yml

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -257,27 +257,28 @@ jobs:
257257
echo "has_manifest=true" >> "$GITHUB_OUTPUT"
258258
259259
# Validate TOML structure using Python 3.11+ tomllib
260-
python3 -c "
261-
import tomllib, sys
262-
with open('eclexiaiser.toml', 'rb') as f:
263-
data = tomllib.load(f)
264-
project = data.get('project', {})
265-
if not project.get('name', '').strip():
266-
print('ERROR: project.name is required', file=sys.stderr)
267-
sys.exit(1)
268-
functions = data.get('functions', [])
269-
if not functions:
270-
print('ERROR: at least one [[functions]] entry is required', file=sys.stderr)
271-
sys.exit(1)
272-
for fn in functions:
273-
if not fn.get('name', '').strip():
274-
print('ERROR: function name cannot be empty', file=sys.stderr)
275-
sys.exit(1)
276-
if not fn.get('source', '').strip():
277-
print(f'ERROR: function {fn[\"name\"]} has no source path', file=sys.stderr)
278-
sys.exit(1)
279-
print(f'Valid: {project[\"name\"]} ({len(functions)} function(s))')
280-
" || {
260+
python3 <<'PY' || {
261+
import tomllib, sys
262+
263+
with open('eclexiaiser.toml', 'rb') as f:
264+
data = tomllib.load(f)
265+
project = data.get('project', {})
266+
if not project.get('name', '').strip():
267+
print('ERROR: project.name is required', file=sys.stderr)
268+
sys.exit(1)
269+
functions = data.get('functions', [])
270+
if not functions:
271+
print('ERROR: at least one [[functions]] entry is required', file=sys.stderr)
272+
sys.exit(1)
273+
for fn in functions:
274+
if not fn.get('name', '').strip():
275+
print('ERROR: function name cannot be empty', file=sys.stderr)
276+
sys.exit(1)
277+
if not fn.get('source', '').strip():
278+
print(f'ERROR: function {fn["name"]} has no source path', file=sys.stderr)
279+
sys.exit(1)
280+
print(f'Valid: {project["name"]} ({len(functions)} function(s))')
281+
PY
281282
echo "::error file=eclexiaiser.toml::Invalid eclexiaiser.toml — see step output for details"
282283
exit 1
283284
}
@@ -378,4 +379,3 @@ print(f'Valid: {project[\"name\"]} ({len(functions)} function(s))')
378379
*Generated by the [Dogfood Gate](https://github.com/hyperpolymath/rsr-template-repo) workflow.*
379380
*Dogfooding is guinea pig fooding — we test our tools on ourselves.*
380381
EOF
381-
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
4+
name: FFI Full Integration
5+
6+
permissions:
7+
contents: read
8+
9+
on:
10+
push:
11+
branches: [main]
12+
paths:
13+
- 'src/**'
14+
- 'ffi/zig/**'
15+
- 'proven-ffi.ipkg'
16+
- 'scripts/build-refc.sh'
17+
- '.github/workflows/ffi-full-integration.yml'
18+
pull_request:
19+
branches: [main]
20+
paths:
21+
- 'src/**'
22+
- 'ffi/zig/**'
23+
- 'proven-ffi.ipkg'
24+
- 'scripts/build-refc.sh'
25+
- '.github/workflows/ffi-full-integration.yml'
26+
workflow_dispatch:
27+
28+
concurrency:
29+
group: ffi-full-integration-${{ github.ref }}
30+
cancel-in-progress: true
31+
32+
jobs:
33+
idris-refc-zig:
34+
name: Idris -> RefC -> Zig
35+
runs-on: ubuntu-latest
36+
timeout-minutes: 45
37+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
38+
39+
env:
40+
IDRIS2_VERSION: '0.8.0'
41+
ZIG_VERSION: '0.15.2'
42+
43+
steps:
44+
- name: Checkout proven
45+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
46+
with:
47+
persist-credentials: false
48+
49+
- name: Check language-bridges credential
50+
id: bridge-token
51+
env:
52+
LANGUAGE_BRIDGES_TOKEN: ${{ secrets.LANGUAGE_BRIDGES_TOKEN }}
53+
run: |
54+
set -euo pipefail
55+
if [ -z "$LANGUAGE_BRIDGES_TOKEN" ]; then
56+
echo "available=false" >> "$GITHUB_OUTPUT"
57+
echo "::notice::LANGUAGE_BRIDGES_TOKEN is not configured; skipping private full FFI integration."
58+
else
59+
echo "available=true" >> "$GITHUB_OUTPUT"
60+
fi
61+
62+
- name: Checkout language-bridges
63+
if: steps.bridge-token.outputs.available == 'true'
64+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
65+
with:
66+
repository: nextgen-languages/language-bridges
67+
path: nextgen-languages/language-bridges
68+
token: ${{ secrets.LANGUAGE_BRIDGES_TOKEN }}
69+
persist-credentials: false
70+
71+
- name: Install Idris 2 dependencies
72+
if: steps.bridge-token.outputs.available == 'true'
73+
run: |
74+
set -euo pipefail
75+
sudo apt-get update
76+
sudo apt-get install -y chezscheme libgmp-dev
77+
78+
- name: Cache Idris 2
79+
if: steps.bridge-token.outputs.available == 'true'
80+
id: cache-idris2
81+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
82+
with:
83+
path: |
84+
~/.idris2
85+
~/idris2-${{ env.IDRIS2_VERSION }}
86+
key: idris2-refc-${{ env.IDRIS2_VERSION }}-${{ runner.os }}
87+
88+
- name: Install Idris 2
89+
if: steps.bridge-token.outputs.available == 'true' && steps.cache-idris2.outputs.cache-hit != 'true'
90+
run: |
91+
set -euo pipefail
92+
git clone --depth 1 --branch v${{ env.IDRIS2_VERSION }} https://github.com/idris-lang/Idris2.git ~/idris2-${{ env.IDRIS2_VERSION }}
93+
cd ~/idris2-${{ env.IDRIS2_VERSION }}
94+
make bootstrap SCHEME=chezscheme
95+
make install
96+
97+
- name: Add Idris 2 to PATH
98+
if: steps.bridge-token.outputs.available == 'true'
99+
run: echo "$HOME/.idris2/bin" >> "$GITHUB_PATH"
100+
101+
- name: Setup Zig
102+
if: steps.bridge-token.outputs.available == 'true'
103+
uses: mlugg/setup-zig@e7d1537c378b83b8049f65dda471d87a2f7b2df2 # v1
104+
with:
105+
version: ${{ env.ZIG_VERSION }}
106+
107+
- name: Build Idris RefC output
108+
if: steps.bridge-token.outputs.available == 'true'
109+
run: scripts/build-refc.sh --verbose
110+
111+
- name: Build and test libproven
112+
if: steps.bridge-token.outputs.available == 'true'
113+
run: |
114+
set -euo pipefail
115+
IDRIS2_PREFIX="$(idris2 --prefix)"
116+
REFC_RUNTIME="$(find "$IDRIS2_PREFIX" -path '*/support/refc' -type d 2>/dev/null | head -1)"
117+
C_SUPPORT="$(find "$IDRIS2_PREFIX" -path '*/support/c' -type d 2>/dev/null | head -1)"
118+
SUPPORT_LIB_FILE="$(find "$IDRIS2_PREFIX" -path '*/lib/libidris2_support.*' 2>/dev/null | head -1)"
119+
120+
test -n "$REFC_RUNTIME"
121+
test -n "$C_SUPPORT"
122+
test -n "$SUPPORT_LIB_FILE"
123+
SUPPORT_LIB="$(dirname "$SUPPORT_LIB_FILE")"
124+
test -d "$GITHUB_WORKSPACE/build/refc"
125+
test -d "$REFC_RUNTIME"
126+
test -d "$C_SUPPORT"
127+
test -d "$SUPPORT_LIB"
128+
129+
cd ffi/zig
130+
zig build \
131+
-Didris-refc="$GITHUB_WORKSPACE/build/refc" \
132+
-Didris-refc-runtime="$REFC_RUNTIME" \
133+
-Didris-c-support="$C_SUPPORT" \
134+
-Didris-support-lib="$SUPPORT_LIB"
135+
zig build test \
136+
-Didris-refc="$GITHUB_WORKSPACE/build/refc" \
137+
-Didris-refc-runtime="$REFC_RUNTIME" \
138+
-Didris-c-support="$C_SUPPORT" \
139+
-Didris-support-lib="$SUPPORT_LIB"
140+
ls -la zig-out/lib/libproven.*

0 commit comments

Comments
 (0)