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
31 changes: 31 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Self-contained dev/CI image for EigenRegex.
#
# EigenRegex is a *consumer* of EigenScript: it runs pure .eigs programs (the
# Pike-VM regex engine + tests) against the runtime — there is no build step of
# its own. This image builds EigenScript at a pinned tag and puts it on PATH,
# so the tag below IS the version pin — bump EIGS_REF to move the runtime.
#
# The same image backs Codespaces AND CI (.github/workflows/test.yml runs the
# suite inside it via devcontainers/ci), so "green in my Codespace" and "green
# in CI" can't drift. Building from source (rather than FROM a published runtime
# image) keeps this working for a private repo with no cross-repo package-pull
# credentials.
FROM debian:stable-slim

ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
git \
&& rm -rf /var/lib/apt/lists/*

# Build + install EigenScript at the pinned tag onto /usr/local (binary on
# PATH, stdlib at /usr/local/lib/eigenscript — found relative to the binary).
ARG EIGS_REF=v0.21.0
RUN git clone --depth 1 --branch "${EIGS_REF}" \
https://github.com/InauguralSystems/EigenScript.git /tmp/eigs \
&& make -C /tmp/eigs install PREFIX=/usr/local CC=gcc \
&& rm -rf /tmp/eigs

WORKDIR /workspaces/EigenRegex
17 changes: 17 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "EigenRegex",
"build": { "dockerfile": "Dockerfile" },
"workspaceFolder": "/workspaces/EigenRegex",
"remoteUser": "root",

// The test runner addresses the runtime via $EIGENSCRIPT; export it to the
// installed binary so `bash tests/run.sh` works out of the box in a Codespace.
"postCreateCommand": "eigenscript --version",
"remoteEnv": { "EIGENSCRIPT": "eigenscript" },

"customizations": {
"vscode": {
"settings": { "files.associations": { "*.eigs": "python" } }
}
}
}
26 changes: 26 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
test:
name: suite (in devcontainer)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

# Build the devcontainer (EigenScript pinned by .devcontainer/Dockerfile's
# EIGS_REF) and run the 220-check suite inside it — the same image a
# Codespace opens. EIGENSCRIPT=eigenscript points the runner at the on-PATH
# runtime; tests/run.sh exits non-zero on any FAIL or crash.
- name: Build devcontainer and run suite
uses: devcontainers/ci@v0.3
with:
runCmd: EIGENSCRIPT=eigenscript bash tests/run.sh
36 changes: 20 additions & 16 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,29 +42,30 @@ playground build).

EigenScript is **not** vendored. Pin v0.13.0 minimum (strings
needed `<`/`<=` comparison and `ord of s`, both of which shipped in
v0.13.0 — see GAPS.md for the fix history). **v0.14.2** is the
current tested release.
v0.13.0 — see GAPS.md for the fix history). CI pins the runtime via
`.devcontainer/Dockerfile`'s `EIGS_REF` (currently **v0.21.0**) and
builds it from source — bump that to move the tested runtime.

## Run / test

```bash
EIGS=${EIGENSCRIPT_BIN:-/home/jon/EigenScript/src/eigenscript}

# Smoke (S0): load + public-API end-to-end
$EIGS tests/test_smoke.eigs
CI builds the devcontainer (EigenScript pinned by `EIGS_REF`) and runs
the suite inside it via `devcontainers/ci`, so Codespace and CI can't
drift. The runner exits non-zero on any `FAIL` or crash — that's the
gate (the per-stage `.eigs` files print `OK`/`FAIL` but exit 0 on their
own).

# Per-stage tests (each has its own assertion count)
$EIGS tests/test_s1_literals.eigs
$EIGS tests/test_s2_alt.eigs
$EIGS tests/test_s3_repeat.eigs
$EIGS tests/test_s4_classes.eigs
$EIGS tests/test_s5_anchors_groups.eigs
```bash
# All stages + smoke, with a pass/fail exit code (what CI runs):
EIGENSCRIPT=eigenscript bash tests/run.sh

# Quick all-stages loop:
for t in tests/test_s*.eigs tests/test_smoke.eigs; do $EIGS "$t"; done
# Or against a specific binary, one file at a time:
EIGS=${EIGENSCRIPT_BIN:-eigenscript}
$EIGS tests/test_s1_literals.eigs # ... s2 alt, s3 repeat, s4 classes,
$EIGS tests/test_s5_anchors_groups.eigs # s5 anchors/groups, test_smoke
```

220 test checks across S1–S5, all green.
220 test checks across S1–S5, all green. (`tests/bench_search.eigs` is
a manual timing bench, not part of the gate.)

## Layout

Expand All @@ -76,6 +77,9 @@ for t in tests/test_s*.eigs tests/test_smoke.eigs; do $EIGS "$t"; done
| `lib/regex_vm.eigs` | Pike-VM executor (parallel-thread simulation) |
| `tests/test_s{1..5}_*.eigs` | Per-stage tests (literals → alt → repeat → classes → anchors/groups) |
| `tests/test_smoke.eigs` | S0 end-to-end load + API smoke |
| `tests/run.sh` | Suite runner — runs every test, exits non-zero on any FAIL/crash (the CI gate) |
| `tests/bench_search.eigs` | Manual scaling bench for `re_search` (not a pass/fail gate) |
| `.devcontainer/`, `.github/workflows/test.yml` | Pinned (`EIGS_REF`) devcontainer + CI running the suite |
| `GAPS.md` | Upstream-gap ledger (with fixed/open status per entry) |

## Architecture notes
Expand Down
34 changes: 34 additions & 0 deletions tests/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# Run every EigenRegex test program against $EIGENSCRIPT and fail the build if
# any assertion prints FAIL or any script errors / crashes. The per-stage tests
# print "<label> OK" / "<label> FAIL" lines but exit 0 regardless, so this
# wrapper is what turns them into a CI gate. (bench_search.eigs is a manual
# timing bench, not a test — the glob below excludes it.)
set -uo pipefail
EIGS="${EIGENSCRIPT:-eigenscript}"
cd "$(dirname "$0")/.."

fail=0
total_ok=0
# test_s*.eigs covers test_s1..s5 AND test_smoke; bench_search.eigs is excluded.
for t in tests/test_s*.eigs; do
out=$("$EIGS" "$t" 2>&1); rc=$?
ok=$(printf '%s\n' "$out" | grep -c ' OK$' || true)
bad=$(printf '%s\n' "$out" | grep -c 'FAIL' || true)
if [ "$rc" -ne 0 ] || [ "$bad" -ne 0 ]; then
echo "FAIL: $(basename "$t") (rc=$rc, $bad failing checks)"
printf '%s\n' "$out" | grep -iE 'FAIL|error' | head -5
fail=1
else
echo "PASS: $(basename "$t") ($ok checks)"
total_ok=$((total_ok + ok))
fi
done

echo "---"
if [ "$fail" -eq 0 ]; then
echo "ALL PASSED ($total_ok checks)"
else
echo "SOME FAILED"
fi
exit "$fail"
Loading