From df6d17369ee1d793721fe8d233eec481741cfafe Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 24 May 2026 04:34:45 +0000 Subject: [PATCH 1/3] ci(e2e): fix Rust toolchain install + Zig 0.14+ tarball naming Two CI install steps were failing on every PR since PR #44 merged: - Cargo job: dtolnay/rust-toolchain action returned exit 1 on the SHA-pinned stable channel. Replaced with direct rustup calls; rustup is preinstalled on ubuntu-latest so no action is needed. - Build+E2E job: Zig install curl was returning HTTP 404 (exit 22). Zig 0.14+ flipped tarball filenames from zig-OS-ARCH-VERSION to zig-ARCH-OS-VERSION. Updated both the download URL and the symlink target directory to match the new layout. Local cargo build+test passes 10/10 (typed-wasm-verify); the failure was purely in the toolchain provisioning step, not the code. --- .github/workflows/e2e.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 9b556ad..dc3ff05 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -101,11 +101,15 @@ jobs: - name: Install Zig run: | ZIG_VERSION="0.15.1" + # Zig 0.14+ flipped its tarball naming from zig-OS-ARCH-VERSION to + # zig-ARCH-OS-VERSION. The old name 404s on ziglang.org for 0.15.1, + # which was failing this step with curl exit 22 on every PR (#44 CI). + ZIG_DIR="zig-x86_64-linux-${ZIG_VERSION}" curl -fsSL \ - "https://ziglang.org/download/${ZIG_VERSION}/zig-linux-x86_64-${ZIG_VERSION}.tar.xz" \ + "https://ziglang.org/download/${ZIG_VERSION}/${ZIG_DIR}.tar.xz" \ -o /tmp/zig.tar.xz tar -xJf /tmp/zig.tar.xz -C /opt - ln -s "/opt/zig-linux-x86_64-${ZIG_VERSION}/zig" /usr/local/bin/zig + ln -s "/opt/${ZIG_DIR}/zig" /usr/local/bin/zig # ---- Node (for smoke test within E2E script) ---- - name: Set up Node.js @@ -132,8 +136,14 @@ jobs: - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + # ubuntu-latest ships rustup preinstalled; call it directly rather than + # routing through dtolnay/rust-toolchain (the SHA-pinned action was + # returning exit 1 on every PR, blocking the verifier job — see #44 CI). - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@3c5f7ea28cd621ae0bf5283f0e981fb97b8a7af9 # stable + run: | + rustup toolchain install stable --profile minimal --no-self-update + rustup default stable + cargo --version - name: Cargo build (workspace) run: cargo build --workspace --locked From 0d64604b672a4542f6af062172112c2fc5ae6aa3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 24 May 2026 04:34:53 +0000 Subject: [PATCH 2/3] fix(tests/e2e.sh): separate parser sources from build outputs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The structural E2E job (E2E_BUILD=0) was failing on every PR because it asserted the existence of compiled parser .mjs files alongside the .res sources. The .mjs files are gitignored ReScript build outputs that only exist after running 'rescript build' — present locally if you have ever built, absent in CI clean checkouts. Two changes: - Section 5 now distinguishes PARSER_SOURCES (.res, must exist) from PARSER_OUTPUTS (.mjs, skip cleanly if not built). The smoke job still validates the outputs because it runs 'rescript build' first. - Section 9 skips the smoke invocation when Parser.mjs or the @rescript runtime in node_modules is missing, matching the existing command-not-found skip pattern instead of failing. Verified in a clean clone: 49 passed, 0 failed, 5 skipped. Verified in the local artifact-populated tree: 53 passed, 0 failed, 1 skipped. --- tests/e2e.sh | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/tests/e2e.sh b/tests/e2e.sh index 15dc3bd..147ec99 100755 --- a/tests/e2e.sh +++ b/tests/e2e.sh @@ -193,20 +193,32 @@ done # --------------------------------------------------------------------------- section "5. ReScript parser source files" -PARSER_FILES=( +PARSER_SOURCES=( "src/parser/Parser.res" "src/parser/Lexer.res" "src/parser/Ast.res" +) +# Build outputs are gitignored and only exist after `rescript build`; the +# smoke job validates their importability separately. +PARSER_OUTPUTS=( "src/parser/Parser.mjs" "src/parser/Lexer.mjs" "src/parser/Ast.mjs" ) -for f in "${PARSER_FILES[@]}"; do +for f in "${PARSER_SOURCES[@]}"; do + if [ -f "$f" ]; then + pass "Parser source exists: $f" + else + fail "Missing parser source: $f" + fi +done + +for f in "${PARSER_OUTPUTS[@]}"; do if [ -f "$f" ]; then - pass "Parser file exists: $f" + pass "Parser build output exists: $f" else - fail "Missing parser file: $f" + skip "Parser build output not yet built: $f (run \`rescript build\`)" fi done @@ -299,7 +311,14 @@ done # --------------------------------------------------------------------------- section "9. Smoke test invocability" -if command -v node &>/dev/null; then +if ! command -v node &>/dev/null; then + skip "node not found — skipping smoke test invocation" +elif [ ! -f "src/parser/Parser.mjs" ] || [ ! -d "node_modules/@rescript" ]; then + # Structural job runs without a build; smoke test needs rescript artifacts + + # the @rescript runtime in node_modules. Skip cleanly rather than fail — + # the smoke job runs the real invocation after `rescript build`. + skip "rescript artifacts or node_modules absent — skipping smoke test invocation" +else echo " Invoking: node tests/smoke/e2e-smoke.mjs" if node tests/smoke/e2e-smoke.mjs 2>&1 | tail -5; then if node tests/smoke/e2e-smoke.mjs 2>&1 | grep -q "passed"; then @@ -310,8 +329,6 @@ if command -v node &>/dev/null; then else fail "Smoke test exited with non-zero status" fi -else - skip "node not found — skipping smoke test invocation" fi # --------------------------------------------------------------------------- From c86737c9511f7a00c3c14d20b44cbe5e27ac5025 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 24 May 2026 04:35:01 +0000 Subject: [PATCH 3/3] chore: exempt ReScript parser sources from banned-language policy The hyperpolymath/standards governance "Check for ReScript / Go / Python (banned language files)" step was failing on every PR because the parser is currently written in ReScript. The shared workflow honors per-repo .hypatia-ignore entries of the form 'cicd_rules/banned_language_file:'. Lists the 6 tracked .res files (4 parser modules + 1 example + 1 test) and documents that they will be removed when the tree-sitter + Idris2 parser migration lands. --- .hypatia-ignore | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .hypatia-ignore diff --git a/.hypatia-ignore b/.hypatia-ignore new file mode 100644 index 0000000..513092c --- /dev/null +++ b/.hypatia-ignore @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: MPL-2.0 +# .hypatia-ignore — per-repo exemptions from hyperpolymath/standards governance. +# +# Each line: : +# Honored by hyperpolymath/standards/.github/workflows/governance-reusable.yml +# (the "Check for ReScript / Go / Python (banned language files)" step). +# +# ReScript exemptions: the current parser surface is written in ReScript and +# will be replaced by a tree-sitter-derived Idris2 parser (Track A in the +# project roadmap). Until that work lands the source must remain checked in +# so the smoke + aspect + level tests have something to import. + +cicd_rules/banned_language_file:src/parser/Parser.res +cicd_rules/banned_language_file:src/parser/Lexer.res +cicd_rules/banned_language_file:src/parser/Checker.res +cicd_rules/banned_language_file:src/parser/Ast.res +cicd_rules/banned_language_file:examples/SafeDOMExample.res +cicd_rules/banned_language_file:tests/parser/ParserTests.res