From 8483f9c3060f555abadb89ab55c00c7eb1cfd672 Mon Sep 17 00:00:00 2001 From: InauguralPhysicist Date: Sun, 28 Jun 2026 22:46:45 -0500 Subject: [PATCH] ci: add pinned devcontainer + CI running the suite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EigenRegex had a 220-check suite but no CI — pushes (including the recent O(n^2)->O(n) search fix) were gated only by local runs. Adds the standard eigs-consumer pattern, matching dynamics/liferaft/tidelog: - .devcontainer/Dockerfile builds EigenScript from source at a pinned tag (EIGS_REF=v0.21.0) onto PATH — the tag IS the runtime pin. Same image backs Codespaces and CI, so they can't drift. - .github/workflows/test.yml runs the suite inside that image via devcontainers/ci on push + PR to main. - tests/run.sh is the gate: runs every tests/test_s*.eigs (incl. smoke) and exits non-zero on any FAIL or crash — the per-stage .eigs files print OK/FAIL but exit 0 on their own, so this wrapper is what makes them a gate. Excludes bench_search.eigs (a manual timing bench). Verified locally against v0.21.0: 220 checks pass (rc=0); the runner returns rc=1 when a test fails. CLAUDE.md toolchain/run sections updated (were stale: v0.14.2 + a defunct binary path). Co-Authored-By: Claude Opus 4.8 (1M context) --- .devcontainer/Dockerfile | 31 ++++++++++++++++++++++++++++ .devcontainer/devcontainer.json | 17 ++++++++++++++++ .github/workflows/test.yml | 26 ++++++++++++++++++++++++ CLAUDE.md | 36 ++++++++++++++++++--------------- tests/run.sh | 34 +++++++++++++++++++++++++++++++ 5 files changed, 128 insertions(+), 16 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/workflows/test.yml create mode 100755 tests/run.sh diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..61ab1ac --- /dev/null +++ b/.devcontainer/Dockerfile @@ -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 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..2e2665b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -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" } } + } + } +} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..069a90d --- /dev/null +++ b/.github/workflows/test.yml @@ -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 diff --git a/CLAUDE.md b/CLAUDE.md index 95f6315..f01ac30 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 @@ -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 diff --git a/tests/run.sh b/tests/run.sh new file mode 100755 index 0000000..00ce769 --- /dev/null +++ b/tests/run.sh @@ -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 "