Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/boj-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ jobs:
trigger-boj:
runs-on: ubuntu-latest
timeout-minutes: 15
if: ${{ vars.BOJ_SERVER_URL != '' || secrets.BOJ_SERVER_URL != '' }}
# No job-level `if:` gate here. The `secrets` context is not available in
# job-level conditions (only `github`, `inputs`, `needs`, `vars`), and a
# `vars`-only test would silently skip the secret-configured case. The step
# below reads both and exits 0 when neither is set, which is the documented
# no-op behaviour.
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand Down
57 changes: 33 additions & 24 deletions .github/workflows/dogfood-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -261,31 +261,40 @@ jobs:

echo "has_manifest=true" >> "$GITHUB_OUTPUT"

# Validate TOML structure using Python 3.11+ tomllib
python3 -c "
import tomllib, sys
with open('eclexiaiser.toml', 'rb') as f:
data = tomllib.load(f)
project = data.get('project', {})
if not project.get('name', '').strip():
print('ERROR: project.name is required', file=sys.stderr)
sys.exit(1)
functions = data.get('functions', [])
if not functions:
print('ERROR: at least one [[functions]] entry is required', file=sys.stderr)
sys.exit(1)
for fn in functions:
if not fn.get('name', '').strip():
print('ERROR: function name cannot be empty', file=sys.stderr)
sys.exit(1)
if not fn.get('source', '').strip():
print(f'ERROR: function {fn[\"name\"]} has no source path', file=sys.stderr)
sys.exit(1)
print(f'Valid: {project[\"name\"]} ({len(functions)} function(s))')
" || {
echo "::error file=eclexiaiser.toml::Invalid eclexiaiser.toml — see step output for details"
# Validate TOML structure using Python 3.11+ tomllib.
# Quoted heredoc: no shell expansion, so no backslash escaping is
# needed. Body is indented to the block scalar's level and is
# dedented back to column 0 by YAML before bash sees it.
if ! python3 <<'PY'
import tomllib, sys

with open('eclexiaiser.toml', 'rb') as f:
data = tomllib.load(f)

project = data.get('project', {})
if not project.get('name', '').strip():
print('ERROR: project.name is required', file=sys.stderr)
sys.exit(1)

functions = data.get('functions', [])
if not functions:
print('ERROR: at least one [[functions]] entry is required', file=sys.stderr)
sys.exit(1)

for fn in functions:
if not fn.get('name', '').strip():
print('ERROR: function name cannot be empty', file=sys.stderr)
sys.exit(1)
if not fn.get('source', '').strip():
print(f"ERROR: function {fn['name']} has no source path", file=sys.stderr)
sys.exit(1)

print(f"Valid: {project['name']} ({len(functions)} function(s))")
PY
then
echo "::error file=eclexiaiser.toml::Invalid eclexiaiser.toml - see step output for details"
exit 1
}
fi

- name: Write summary
run: |
Expand Down
219 changes: 60 additions & 159 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -1,186 +1,87 @@
# SPDX-License-Identifier: MPL-2.0
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
#
# RSR Standard E2E + Aspect + Benchmark Workflow Template
# KRL — E2E, Aspect and Benchmark gates.
#
# Covers ALL merge requirement test categories:
# - E2E (end-to-end pipeline tests)
# - Aspect (cross-cutting concern validation)
# - Benchmarks (performance regression detection)
# - Readiness (Component Readiness Grade: D/C/B)
#
# INSTRUCTIONS: Uncomment and customise the section matching your stack.
# Delete sections that don't apply. See examples in each job.

# These run the checked-in scripts under tests/ and benches/. Each script is
# expected to exit non-zero on failure; none of them are permitted to report
# success without having executed at least one assertion.
name: E2E + Aspect + Bench
on:
push:
branches: [main, master, develop]
paths:
- 'src/**'
- 'ffi/**'
- 'tests/**'
- 'benches/**'
- 'spec/**'
- 'examples/**'
- '.github/workflows/e2e.yml'
pull_request:
branches: [main, master]
paths:
- 'src/**'
- 'ffi/**'
- 'tests/**'
- 'benches/**'
- 'spec/**'
- 'examples/**'
- '.github/workflows/e2e.yml'
workflow_dispatch:
permissions: read-all
concurrency:
group: e2e-${{ github.ref }}
cancel-in-progress: true
jobs:
# ─── End-to-End Tests ──────────────────────────────────────────────
# Uncomment ONE of the following e2e job blocks matching your stack.

## === RUST E2E ===
# e2e:
# name: E2E — Full Pipeline
# runs-on: ubuntu-latest
# timeout-minutes: 15
# steps:
# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# - uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable
# - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2
# - run: cargo build --release
# - run: bash tests/e2e.sh
# # OR: cargo test --test end_to_end -- --nocapture

## === ZIG FFI E2E ===
# e2e:
# name: E2E — FFI Pipeline
# runs-on: ubuntu-latest
# timeout-minutes: 15
# steps:
# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# - uses: goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406 # v2.2.1
# with:
# version: 0.15.0
# - run: cd ffi/zig && zig build test
# - run: bash tests/e2e.sh

## === ELIXIR E2E ===
# e2e:
# name: E2E — Full Pipeline
# runs-on: ubuntu-latest
# timeout-minutes: 15
# steps:
# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# - uses: erlef/setup-beam@fc68ffb90438ef2936bbb3251622353b3dcb2f93 # v1.24.0
# with:
# otp-version: '27.0'
# elixir-version: '1.17'
# - run: mix deps.get && mix compile --warnings-as-errors
# - run: mix test test/integration/e2e_test.exs --trace

## === DENO/RESCRIPT E2E ===
# e2e:
# name: E2E — Full Pipeline
# runs-on: ubuntu-latest
# timeout-minutes: 15
# steps:
# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# - uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4
# with:
# deno-version: v2.x
# - run: deno install --node-modules-dir=auto
# - run: deno task res:build # ReScript compile
# - run: deno test tests/e2e/
env:
ZIG_VERSION: 0.16.0

## === PLAYWRIGHT (Browser E2E) ===
# e2e-playwright:
# name: Playwright — ${{ matrix.project }}
# runs-on: ubuntu-latest
# timeout-minutes: 20
# strategy:
# fail-fast: false
# matrix:
# project: [chromium-1080p, firefox-1080p, webkit-1080p]
# steps:
# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# - uses: denoland/setup-deno@667a34cdef165d8d2b2e98dde39547c9daac7282 # v2.0.4
# with:
# deno-version: v2.x
# - run: deno install --node-modules-dir=auto
# - run: npx playwright install --with-deps
# - run: npx playwright test --project=${{ matrix.project }}
# - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
# if: failure()
# with:
# name: playwright-traces-${{ matrix.project }}
# path: test-results/**/trace.zip
# retention-days: 7

## === HASKELL E2E ===
# e2e:
# name: E2E — Full Pipeline
# runs-on: ubuntu-latest
# timeout-minutes: 15
# steps:
# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# - uses: haskell-actions/setup@cd0d9bdd65b20557f41bea4dbe43d0b5fbbfe553 # v2.11.0
# with:
# ghc-version: '9.6'
# cabal-version: '3.10'
# - run: cabal build all
# - run: bash tests/integration-test.sh

# ─── Aspect Tests ──────────────────────────────────────────────────
# Cross-cutting concerns: thread safety, ABI contracts, SPDX, dangerous patterns
# Uncomment and customise:

# aspect-tests:
# name: Aspect — Architectural Invariants
# runs-on: ubuntu-latest
# timeout-minutes: 10
# steps:
# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# - run: bash tests/aspect_tests.sh

# ─── Benchmarks ────────────────────────────────────────────────────
# Performance regression detection. Uncomment matching stack:

## === RUST BENCH ===
# benchmarks:
# name: Bench — Performance Regression
# runs-on: ubuntu-latest
# timeout-minutes: 15
# steps:
# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# - uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable
# - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2
# - run: cargo bench 2>&1 | tee /tmp/bench-results.txt
# - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
# if: always()
# with:
# name: benchmark-results
# path: /tmp/bench-results.txt
# retention-days: 30
jobs:
e2e:
name: E2E — FFI build, ABI correspondence, grammar smoke
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Zig
uses: goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406 # v2.2.1
with:
version: ${{ env.ZIG_VERSION }}
- name: Run E2E suite
run: bash tests/e2e.sh

## === ZIG BENCH ===
# benchmarks:
# name: Bench — Performance Regression
# runs-on: ubuntu-latest
# timeout-minutes: 15
# steps:
# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# - uses: goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406 # v2.2.1
# with:
# version: 0.15.0
# - run: cd ffi/zig && zig build bench
aspect:
name: Aspect — architectural invariants
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run aspect tests
run: bash tests/aspect_tests.sh

# ─── Readiness (CRG) ──────────────────────────────────────────────
# Component Readiness Grade: D (runs) → C (correct) → B (edge cases)
smoke:
name: Smoke — KRL grammar and examples
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Run grammar smoke suite
run: bash tests/smoke/grammar_smoke.sh

# readiness:
# name: Readiness — Grade D/C/B
# runs-on: ubuntu-latest
# timeout-minutes: 10
# steps:
# - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
# - uses: dtolnay/rust-toolchain@4be9e76fd7c4901c61fb841f559994984270fce7 # stable
# - uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2
# - run: cargo test --test readiness -- --nocapture
ffi-unit:
name: FFI — Zig unit tests
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Set up Zig
uses: goto-bus-stop/setup-zig@abea47f85e598557f500fa1fd2ab7464fcb39406 # v2.2.1
with:
version: ${{ env.ZIG_VERSION }}
- name: zig build test
run: cd src/interface/ffi && zig build test
- name: zig build
run: cd src/interface/ffi && zig build
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ sync_report*.txt
# Hypatia scan cache (local-only)
.hypatia/
.zig-cache/
zig-out/
target/
node_modules/
_build/
Expand Down
8 changes: 8 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@ repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
# LICENSES/ holds verbatim licence texts that must match the canonical
# SPDX wording byte for byte (CC-BY-SA-4.0 and MPL-2.0 both contain
# significant trailing whitespace). Reformatting them silently
# invalidates REUSE conformance, so they are excluded here.
- id: trailing-whitespace
exclude: '^LICENSES/'
- id: end-of-file-fixer
exclude: '^LICENSES/'
- id: check-yaml
- id: check-json
- id: check-toml
Expand Down Expand Up @@ -47,5 +53,7 @@ repos:
exclude: '(\.git|node_modules|target|_build|deps|\.deno|external_corpora|\.lake)/'

# --- Secret detection ---
- repo: https://github.com/gitleaks/gitleaks
rev: v8.24.3
hooks:
- id: gitleaks
18 changes: 9 additions & 9 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Uncomment and customize for your project
# rust nightly
# just 1.40.0
# nickel 1.10.0
# gleam 1.8.0
# elixir 1.18.0
# erlang 27.2
# zig 0.14.0
# idris2 0.7.0
# Toolchains this repository actually uses.
# zig — the C-ABI FFI shim under src/interface/ffi (verified against 0.16.0)
# idris2 — the ABI declarations under src/interface/Abi
#
# NOTE: `rust nightly` is retained only because .github/workflows/rust-ci.yml
# still calls the shared Rust reusable. There is no Cargo.toml in this repo,
# so that workflow currently gates nothing — see issue #38.
zig 0.16.0
idris2 0.7.0
rust nightly
2 changes: 1 addition & 1 deletion LICENSES/MPL-2.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ Exhibit A - Source Code Form License Notice

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
file, You can obtain one at https://mozilla.org/MPL/2.0/.

If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
Expand Down
Loading
Loading