Skip to content

Commit 9a4cd1e

Browse files
committed
feat(nim-binding): stand up detachable CI + tests — binding-tier-1
1 parent 2bb8ba8 commit 9a4cd1e

9 files changed

Lines changed: 567 additions & 1 deletion

File tree

.github/workflows/nim-ci.yml

Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
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+
# Nim CI — proven binding-tier-1.
5+
#
6+
# This workflow follows the DETACHABLE-HARNESS contract for proven binding CI.
7+
# It references ONLY: bindings/nim/, ffi/zig/, and bindings/c/include/proven.h.
8+
9+
name: Nim CI
10+
11+
permissions:
12+
contents: read
13+
14+
on:
15+
push:
16+
branches: [main]
17+
paths:
18+
- 'bindings/nim/**'
19+
- 'ffi/zig/**'
20+
- 'bindings/c/include/proven.h'
21+
- '.github/workflows/nim-ci.yml'
22+
pull_request:
23+
branches: [main]
24+
paths:
25+
- 'bindings/nim/**'
26+
- 'ffi/zig/**'
27+
- 'bindings/c/include/proven.h'
28+
- '.github/workflows/nim-ci.yml'
29+
workflow_dispatch:
30+
31+
concurrency:
32+
group: nim-ci-${{ github.ref }}
33+
cancel-in-progress: true
34+
35+
env:
36+
# Pinned Nim toolchain (Ubuntu 22.04 amd64).
37+
NIM_VERSION: '2.0.4'
38+
NIM_URL: 'https://nim-lang.org/download/nim-2.0.4-linux_x64.tar.xz'
39+
NIM_SHA256: '2ca6e7b701bdfee2e7a8def2f0f5eeba026420c612261faa3d4a85be04c679b5'
40+
ZIG_VERSION: '0.15.2'
41+
42+
jobs:
43+
detachability-guard:
44+
runs-on: ubuntu-22.04
45+
timeout-minutes: 2
46+
steps:
47+
- name: Checkout (workflow file only)
48+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
49+
with:
50+
sparse-checkout: |
51+
.github/workflows/nim-ci.yml
52+
sparse-checkout-cone-mode: false
53+
54+
- name: Detachability grep guard
55+
run: |
56+
set -euo pipefail
57+
forbidden=0
58+
while IFS= read -r raw; do
59+
p="${raw%[.,;:)]}"
60+
case "$p" in
61+
bindings/nim/*|ffi/zig/*|bindings/c/include|bindings/c/include/proven.h|.github/workflows/nim-ci.yml)
62+
;;
63+
bindings/*/*)
64+
echo "::error file=.github/workflows/nim-ci.yml::detachability violation: $p"
65+
forbidden=1
66+
;;
67+
tests/*)
68+
echo "::error file=.github/workflows/nim-ci.yml::detachability violation (root-level tests/): $p"
69+
forbidden=1
70+
;;
71+
esac
72+
done < <(grep -oE '(bindings/[a-zA-Z0-9_-]+/[A-Za-z0-9_./-]*|tests/[A-Za-z0-9_./-]+)' \
73+
.github/workflows/nim-ci.yml | sort -u)
74+
[ "$forbidden" -eq 0 ]
75+
echo "detachability-guard: OK"
76+
77+
nim-build:
78+
runs-on: ubuntu-22.04
79+
needs: detachability-guard
80+
timeout-minutes: 20
81+
82+
steps:
83+
- name: Checkout proven
84+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
85+
with:
86+
path: proven
87+
88+
- name: Setup Zig ${{ env.ZIG_VERSION }}
89+
uses: mlugg/setup-zig@e7d1537c378b83b8049f65dda471d87a2f7b2df2 # v1
90+
with:
91+
version: ${{ env.ZIG_VERSION }}
92+
93+
- name: Build libproven.so (WIRED-subset fixture)
94+
working-directory: proven/ffi/zig
95+
run: |
96+
set -euo pipefail
97+
mkdir -p zig-out/lib
98+
zig build-lib -dynamic -O ReleaseSafe \
99+
-fPIC \
100+
--name proven \
101+
-femit-bin=zig-out/lib/libproven.so \
102+
src/main.zig -lc
103+
nm -D --defined-only zig-out/lib/libproven.so \
104+
| awk '$2 ~ /^[TWV]$/ { print $3 }' \
105+
| grep -E '^(proven_path_has_traversal|proven_header_has_crlf|proven_free_string|proven_version|proven_build_info)$' \
106+
| sort -u | tee /tmp/wired-exports.txt
107+
test "$(wc -l < /tmp/wired-exports.txt)" -eq 5
108+
109+
- name: Install Nim ${{ env.NIM_VERSION }}
110+
run: |
111+
set -euo pipefail
112+
curl -fsSL --retry 3 -o /tmp/nim.tar.xz "$NIM_URL"
113+
echo "$NIM_SHA256 /tmp/nim.tar.xz" | sha256sum --check
114+
mkdir -p /opt/nim
115+
tar -xJf /tmp/nim.tar.xz -C /opt/nim --strip-components=1
116+
echo "/opt/nim/bin" >> $GITHUB_PATH
117+
/opt/nim/bin/nim --version
118+
119+
- name: Nim check
120+
working-directory: proven/bindings/nim
121+
run: |
122+
set -euo pipefail
123+
nim check src/proven.nim
124+
125+
- name: Upload libproven.so artefact
126+
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4
127+
with:
128+
name: libproven-x86_64-linux-gnu
129+
path: proven/ffi/zig/zig-out/lib/libproven.so
130+
if-no-files-found: error
131+
retention-days: 1
132+
133+
nim-symbol-audit:
134+
runs-on: ubuntu-22.04
135+
needs: nim-build
136+
timeout-minutes: 5
137+
steps:
138+
- name: Checkout (binding only)
139+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
140+
with:
141+
sparse-checkout: |
142+
bindings/nim
143+
sparse-checkout-cone-mode: false
144+
145+
- name: Download libproven.so
146+
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
147+
with:
148+
name: libproven-x86_64-linux-gnu
149+
path: lib/
150+
151+
- name: Run nim-symbol-audit
152+
working-directory: bindings/nim
153+
env:
154+
PROVEN_LIB_PATH: ${{ github.workspace }}/lib
155+
run: ./scripts/symbol-audit.sh
156+
157+
nim-smoke:
158+
runs-on: ubuntu-22.04
159+
needs: nim-build
160+
timeout-minutes: 10
161+
steps:
162+
- name: Checkout (binding + C header only)
163+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
164+
with:
165+
sparse-checkout: |
166+
bindings/nim
167+
bindings/c/include
168+
.github/workflows/nim-ci.yml
169+
sparse-checkout-cone-mode: false
170+
171+
- name: Install Nim ${{ env.NIM_VERSION }}
172+
run: |
173+
set -euo pipefail
174+
curl -fsSL --retry 3 -o /tmp/nim.tar.xz "$NIM_URL"
175+
echo "$NIM_SHA256 /tmp/nim.tar.xz" | sha256sum --check
176+
mkdir -p /opt/nim
177+
tar -xJf /tmp/nim.tar.xz -C /opt/nim --strip-components=1
178+
echo "/opt/nim/bin" >> $GITHUB_PATH
179+
180+
- name: Install just
181+
uses: extractions/setup-just@dd310ad5a97d8e7b41793f8ef055398d51ad4de6 # v2
182+
with:
183+
just-version: '1.36.0'
184+
185+
- name: Download libproven.so
186+
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
187+
with:
188+
name: libproven-x86_64-linux-gnu
189+
path: lib/
190+
191+
- name: Build + Run hello_smoke
192+
working-directory: bindings/nim
193+
env:
194+
PROVEN_LIB_PATH: ${{ github.workspace }}/lib
195+
PROVEN_INCLUDE_PATH: ${{ github.workspace }}/bindings/c/include
196+
LD_LIBRARY_PATH: ${{ github.workspace }}/lib
197+
run: just build && ./smoke/hello_smoke
198+
199+
nim-tests:
200+
runs-on: ubuntu-22.04
201+
needs: nim-build
202+
timeout-minutes: 15
203+
steps:
204+
- name: Checkout (binding + C header only)
205+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
206+
with:
207+
sparse-checkout: |
208+
bindings/nim
209+
bindings/c/include
210+
.github/workflows/nim-ci.yml
211+
sparse-checkout-cone-mode: false
212+
213+
- name: Install Nim ${{ env.NIM_VERSION }}
214+
run: |
215+
set -euo pipefail
216+
curl -fsSL --retry 3 -o /tmp/nim.tar.xz "$NIM_URL"
217+
echo "$NIM_SHA256 /tmp/nim.tar.xz" | sha256sum --check
218+
mkdir -p /opt/nim
219+
tar -xJf /tmp/nim.tar.xz -C /opt/nim --strip-components=1
220+
echo "/opt/nim/bin" >> $GITHUB_PATH
221+
222+
- name: Install just
223+
uses: extractions/setup-just@dd310ad5a97d8e7b41793f8ef055398d51ad4de6 # v2
224+
with:
225+
just-version: '1.36.0'
226+
227+
- name: Download libproven.so
228+
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
229+
with:
230+
name: libproven-x86_64-linux-gnu
231+
path: lib/
232+
233+
- name: Run all Nim tests
234+
working-directory: bindings/nim
235+
env:
236+
PROVEN_LIB_PATH: ${{ github.workspace }}/lib
237+
PROVEN_INCLUDE_PATH: ${{ github.workspace }}/bindings/c/include
238+
LD_LIBRARY_PATH: ${{ github.workspace }}/lib
239+
run: just test
240+
241+
nim-detachable-build:
242+
runs-on: ubuntu-22.04
243+
needs: nim-build
244+
timeout-minutes: 15
245+
steps:
246+
- name: Sparse checkout (detachable set only)
247+
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
248+
with:
249+
sparse-checkout: |
250+
bindings/nim
251+
bindings/c/include/proven.h
252+
.github/workflows/nim-ci.yml
253+
sparse-checkout-cone-mode: false
254+
path: proven-nim
255+
256+
- name: Verify detached layout (no other proven trees present)
257+
working-directory: proven-nim
258+
run: |
259+
set -euo pipefail
260+
if [ -d "src" ] || [ -d "ffi" ] || [ -d "tests" ] || [ -d "audits" ]; then
261+
echo "::error::detachability violation: non-binding trees present in detached checkout"
262+
ls -la
263+
exit 1
264+
fi
265+
test -d bindings/nim

Justfile

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,23 @@ chapel-test: build-ffi
180180
chapel-clean:
181181
just -d bindings/chapel --justfile bindings/chapel/Justfile clean
182182

183+
# --- Nim Binding (Tier 1 Detachable Harness) ---
184+
# These recipes are THIN FORWARDERS into bindings/nim/.
185+
nim-check: build-ffi
186+
PROVEN_LIB_PATH="$(pwd)/ffi/zig/zig-out/lib" \
187+
just -d bindings/nim --justfile bindings/nim/Justfile nim-check
188+
189+
nim-build: build-ffi
190+
PROVEN_LIB_PATH="$(pwd)/ffi/zig/zig-out/lib" \
191+
just -d bindings/nim --justfile bindings/nim/Justfile build
192+
193+
nim-test: build-ffi
194+
PROVEN_LIB_PATH="$(pwd)/ffi/zig/zig-out/lib" \
195+
just -d bindings/nim --justfile bindings/nim/Justfile test
196+
197+
nim-clean:
198+
just -d bindings/nim --justfile bindings/nim/Justfile clean
199+
183200
# ---------------------------------------------------------------------------
184201
# Cleaning
185202
# ---------------------------------------------------------------------------

bindings/nim/Justfile

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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+
# proven-nim binding — detachable build harness.
5+
#
6+
# This Justfile contains every recipe needed to build, audit, and test the
7+
# Nim binding. It is self-contained: it MUST NOT reference recipes
8+
# from the proven repo's root Justfile, nor reach outside the binding's
9+
# directory tree. The libproven.so artefact is supplied via the
10+
# PROVEN_LIB_PATH environment variable so that this harness works in three
11+
# layouts:
12+
#
13+
# 1. in-tree (PROVEN_LIB_PATH=../../ffi/zig/zig-out/lib)
14+
# 2. installed (PROVEN_LIB_PATH=/usr/local/lib)
15+
# 3. standalone proven-nim checkout (PROVEN_LIB_PATH=<external path>)
16+
#
17+
# proven.nimble is the package-metadata source of truth (license, dependencies).
18+
19+
default:
20+
@just --list
21+
22+
# Fast pre-build gate: confirm libproven.so exports every WIRED symbol in
23+
# symbol-manifest.txt. Runs in < 5s; no compilation.
24+
check:
25+
#!/usr/bin/env bash
26+
set -euo pipefail
27+
if [ -z "${PROVEN_LIB_PATH:-}" ]; then
28+
echo "ERROR: set PROVEN_LIB_PATH to the directory containing libproven.so" >&2
29+
exit 2
30+
fi
31+
./scripts/symbol-audit.sh
32+
33+
# Typecheck the Nim modules without producing an executable.
34+
nim-check:
35+
@echo "[nim] typecheck (nim check)..."
36+
nim check src/proven.nim
37+
38+
# Build the smoke target against PROVEN_LIB_PATH/libproven.so.
39+
build: check
40+
#!/usr/bin/env bash
41+
set -euo pipefail
42+
nim c -o:smoke/hello_smoke \
43+
--path:src \
44+
--passL:"-L$PROVEN_LIB_PATH" --passL:"-lproven" \
45+
--passL:"-Wl,-rpath,$PROVEN_LIB_PATH" \
46+
smoke/hello_smoke.nim
47+
echo "[nim] hello_smoke built: smoke/hello_smoke"
48+
49+
# Build + run smoke + every WIRED per-module test in tests/.
50+
# Fails on the first red test.
51+
test: check
52+
#!/usr/bin/env bash
53+
set -euo pipefail
54+
LIB="$PROVEN_LIB_PATH"
55+
echo "[nim] Building hello_smoke..."
56+
nim c -o:smoke/hello_smoke \
57+
--path:src \
58+
--passL:"-L$LIB" --passL:"-lproven" \
59+
--passL:"-Wl,-rpath,$LIB" \
60+
smoke/hello_smoke.nim
61+
echo "[nim] === hello_smoke ==="
62+
./smoke/hello_smoke
63+
for t in tests/Test*.nim; do
64+
name="$(basename "${t%.nim}")"
65+
echo "[nim] === $name ==="
66+
nim c -o:"tests/$name" \
67+
--path:src \
68+
--passL:"-L$LIB" --passL:"-lproven" \
69+
--passL:"-Wl,-rpath,$LIB" \
70+
"$t"
71+
"./tests/$name"
72+
done
73+
echo "[nim] All Nim tests passed."
74+
75+
# Clean compiled binaries; leaves sources untouched.
76+
clean:
77+
#!/usr/bin/env bash
78+
set -euo pipefail
79+
rm -f smoke/hello_smoke
80+
find tests -maxdepth 1 -type f -perm -u+x ! -name '*.nim' -delete 2>/dev/null || true
81+
find . -name '*.tmp' -delete 2>/dev/null || true
82+
echo "[nim] Clean complete."

0 commit comments

Comments
 (0)