Skip to content
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
21 changes: 21 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
# Auto detect text files and perform LF normalization
* text=auto

# Template fixtures are byte-significant for golden comparisons: their raw newlines flow
# into rendered output. Pin them to LF on every platform so Windows checkouts don't smudge
# them to CRLF and break fixture-derived golden tests (goldens are read with \r\n -> \n).
*.heddle text eol=lf

# Checked-in golden / fixture / snapshot data is byte-compared against LF engine output on BOTH
# ubuntu-latest and windows-latest CI. Some comparisons are verbatim (raw Assert.Equal, no \r\n
# normalization); rendered output is LF because the .heddle templates above are LF-pinned. With
# core.autocrlf=true a Windows checkout would smudge these to CRLF and diverge from the LF actual
# output — failing on Windows while Linux (LF everywhere) passes. Pin every such root to LF so the
# byte comparison holds identically on both platforms regardless of the developer's autocrlf setting.
# All files under these roots are text (verified: no binaries). New golden/fixture roots MUST be
# added here in the same change that introduces them — see docs/spec/common/testing-standards.md.
samples/**/golden/** text eol=lf
src/Heddle.Tests/TestTemplate/** text eol=lf
src/Heddle.Tests/Streaming/** text eol=lf
src/Heddle.Generator.Tests/Snapshots/** text eol=lf
src/Heddle.Generator.IntegrationTests/Fixtures/** text eol=lf
src/Heddle.LanguageServices.Tests/Corpus/** text eol=lf
src/Heddle.Demo.Wasm/contract-fixtures/** text eol=lf

# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
Expand Down
81 changes: 73 additions & 8 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Builds the VitePress documentation site from docs/*.md and deploys it to GitHub
# Builds the VitePress documentation site (incl. the WASM demo bundle) and deploys it to GitHub
# Pages. One-time setup: repo Settings -> Pages -> Source = "GitHub Actions".
# PRs build + smoke-test the site (incl. the demo) but never deploy; only main deploys.

name: Heddle Docs

Expand All @@ -9,9 +10,23 @@ on:
paths:
- 'docs/**'
- '.github/workflows/docs.yml'
# The live demo embeds the Ace editor bundle, built from these sources.
- 'src/Heddle.Language/js/**'
- 'src/Heddle.Language/build_ace.sh'
# The demo WASM bundle embeds the engine + facade (build_ace.sh and js/** are covered by the
# whole-project globs below, which supersede the old entries):
- 'src/Heddle/**'
- 'src/Heddle.Language/**'
- 'src/Heddle.LanguageServices/**'
- 'src/Heddle.Demo.Models/**'
- 'src/Heddle.Demo.Wasm/**'
pull_request:
branches: [ "main" ]
paths: # identical list, repeated verbatim — GitHub Actions
- 'docs/**' # path filters are per-trigger; keep the two lists
- '.github/workflows/docs.yml' # in sync when either changes
- 'src/Heddle/**'
- 'src/Heddle.Language/**'
- 'src/Heddle.LanguageServices/**'
- 'src/Heddle.Demo.Models/**'
- 'src/Heddle.Demo.Wasm/**'
workflow_dispatch:

permissions:
Expand All @@ -35,35 +50,85 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v6
with:
node-version: 22
node-version: 24
cache: npm
cache-dependency-path: docs/package-lock.json
# ---- WS1 (D-D): regenerate the JS ANTLR grammar + propagate into the mode copy and
# fail the build on any drift against the checked-in files. This is the single drift
# guard: it regenerates js/*.js from the v2 .g4 (needs Java) and re-runs the
# deterministic propagation into js/src/mode/heddle/, then git diff --exit-code.
- name: Setup Java (ANTLR)
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '23'
- name: Regenerate + propagate JS grammar (drift guard)
working-directory: src/Heddle.Language
run: |
ANTLR_JAR=antlr-4.13.1-complete.jar
if [ ! -f "$ANTLR_JAR" ]; then
curl -fSL -o "$ANTLR_JAR" "https://www.antlr.org/download/$ANTLR_JAR"
fi
java -jar "$ANTLR_JAR" -Dlanguage=JavaScript HeddleLexer.g4 HeddleParser.g4 -o js -lib js -package Heddle.Language
node propagate_js.js
- name: Fail on generated-grammar drift
run: git diff --exit-code -- src/Heddle.Language/js
# Build the custom Ace editor bundle (Ace + Heddle mode + ANTLR JS parser) and stage it
# into docs/public so VitePress copies it to the site root. Serves the demo at
# /Heddle/demo.html with the worker loaded same-origin from /Heddle/ace/. No Java needed
# (the JS parser under js/ is already generated; the script only assembles the bundle).
# into docs/public so VitePress copies it to the site root. The JS parser under js/ was
# just regenerated + propagated above; the script only assembles the bundle.
- name: Build Ace editor bundle
working-directory: src/Heddle.Language
run: bash build_ace.sh
- name: Stage Ace bundle into docs/public
run: |
mkdir -p docs/public/ace
cp -R src/Heddle.Language/ace_build/ace/build/src-noconflict/. docs/public/ace/
# ---- Phase 9: the typed WASM demo bundle (module worker + _framework), Roslyn-free ----
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
- name: Install wasm-tools workload
run: dotnet workload install wasm-tools
- name: Publish demo WASM host
run: dotnet publish src/Heddle.Demo.Wasm -c Release
- name: Stage demo bundle into docs/public
run: |
mkdir -p docs/public/demo
cp -R src/Heddle.Demo.Wasm/bin/Release/net10.0/publish/wwwroot/. docs/public/demo/
- name: Demo bundle budget and purity gate
run: |
size=$(du -sb docs/public/demo | cut -f1)
echo "demo bundle: $size bytes"
test "$size" -le 12582912 || { echo "::error::demo bundle exceeds 12 MB budget"; exit 1; }
if find docs/public/demo -name 'Microsoft.CodeAnalysis*' | grep -q .; then
echo "::error::Roslyn assets found in the demo bundle (D4 regression)"; exit 1
fi
- name: Install dependencies
working-directory: docs
run: npm ci
- name: Build site
working-directory: docs
run: npm run docs:build
# ---- Phase 9: the demo smoke suite (chromium only) against the built site ----
- name: Install Playwright chromium
working-directory: docs
run: npx playwright install chromium --with-deps
- name: Demo smoke tests
working-directory: docs
run: npm run demo:smoke
- name: Configure Pages
if: github.event_name != 'pull_request'
uses: actions/configure-pages@v6
- name: Upload artifact
if: github.event_name != 'pull_request'
uses: actions/upload-pages-artifact@v5
with:
path: docs/.vitepress/dist

deploy:
needs: build
if: github.event_name != 'pull_request'
runs-on: ubuntu-latest
environment:
name: github-pages
Expand Down
16 changes: 9 additions & 7 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
dotnet-version: |
6.0.x
8.0.x
10.0.x
global-json-file: global.json
- name: Restore dependencies
run: dotnet restore
- name: Build
Expand All @@ -57,13 +57,15 @@ jobs:
dotnet-version: |
6.0.x
8.0.x
10.0.x
global-json-file: global.json
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore -c Release
# No `dotnet build --no-build` split here: Heddle.Tool / Heddle.LanguageServer are RID-specific
# .NET tools (PackAsTool + multiple RuntimeIdentifiers), so `dotnet pack` must publish each RID.
# A RID-less `dotnet build` never produces those per-RID outputs, so `--no-build` fails with
# MSB3030 (missing per-RID assemblies) / NETSDK1085. Let pack build+publish, like the release job.
- name: Pack beta packages
run: dotnet pack -c Release --no-build -o ${{ runner.temp }}/pkg --version-suffix "-beta.${{ github.run_number }}" -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg
run: dotnet pack -c Release -o ${{ runner.temp }}/pkg --version-suffix "-beta.${{ github.run_number }}"
- name: NuGet login (OIDC -> short-lived API key)
uses: NuGet/login@v1
id: nuget-login
Expand All @@ -89,14 +91,14 @@ jobs:
dotnet-version: |
6.0.x
8.0.x
10.0.x
global-json-file: global.json
- name: Resolve version from tag
id: ver
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
- name: Restore dependencies
run: dotnet restore
- name: Pack release packages
run: dotnet pack -c Release -p:Version=${{ steps.ver.outputs.version }} -o ${{ runner.temp }}/pkg -p:IncludeSymbols=true -p:SymbolPackageFormat=snupkg
run: dotnet pack -c Release -p:Version=${{ steps.ver.outputs.version }} -o ${{ runner.temp }}/pkg
- name: NuGet login (OIDC -> short-lived API key)
uses: NuGet/login@v1
id: nuget-login
Expand Down
103 changes: 103 additions & 0 deletions .github/workflows/lsp.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Builds and tests the Heddle language services (facade + LSP server), packs the version-pinned
# per-RID dotnet tool, and packages the VS Code extension. Phase 6 (editor tooling / LSP).

name: Heddle LSP

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

permissions:
contents: read

jobs:
test:
runs-on: windows-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-dotnet@v5
with:
# The multi-targeted test suite runs net6.0/net8.0/net10.0/net48; the runner image
# no longer ships the EOL .NET 6 (and may drop 8) runtime, so install them explicitly
# alongside the global.json SDK — otherwise the net6.0 test host aborts (NETCore.App 6 missing).
dotnet-version: |
6.0.x
8.0.x
global-json-file: global.json
- name: Build (Release)
run: dotnet build -c Release Heddle.sln
- name: Test engine suite
run: dotnet test src/Heddle.Tests/Heddle.Tests.csproj -c Release --no-build
- name: Test language services
run: dotnet test src/Heddle.LanguageServices.Tests/Heddle.LanguageServices.Tests.csproj -c Release --no-build
- uses: actions/setup-node@v6
with:
node-version: 24
- name: JS editor-artifact harness (highlight / beautify / completions)
working-directory: src/Heddle.Language
run: |
npm ci
npm test

pack-tool:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
# Framework-dependent + R2R per-RID packs cross-pack from one runner (phase 6 D19).
# -p:EnableToolR2R=true makes PublishReadyToRun visible at the RID-less outer restore so
# the crossgen2 runtime packs for every RuntimeIdentifier are restored (else NETSDK1094).
- name: Pack heddle-lsp (manifest + per-RID + any)
run: dotnet pack src/Heddle.LanguageServer/Heddle.LanguageServer.csproj -c Release -o ./nupkg -p:EnableToolR2R=true
- name: Smoke — install and run --version
run: |
set -euo pipefail
dotnet tool install --global Heddle.LanguageServer --add-source ./nupkg --version 2.0.0 || true
export PATH="$PATH:$HOME/.dotnet/tools"
heddle-lsp --version
- uses: actions/upload-artifact@v7
with:
name: heddle-lsp-nupkg
path: ./nupkg/*.nupkg

vsix:
runs-on: ubuntu-latest
strategy:
matrix:
target: [win32-x64, win32-arm64, linux-x64, linux-arm64, alpine-x64, darwin-x64, darwin-arm64]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
- uses: actions/setup-node@v6
with:
node-version: 24
- name: Map VS Code target to .NET RID
id: rid
run: |
case "${{ matrix.target }}" in
win32-x64) echo "rid=win-x64" >> "$GITHUB_OUTPUT" ;;
win32-arm64) echo "rid=win-arm64" >> "$GITHUB_OUTPUT" ;;
linux-x64) echo "rid=linux-x64" >> "$GITHUB_OUTPUT" ;;
linux-arm64) echo "rid=linux-arm64" >> "$GITHUB_OUTPUT" ;;
alpine-x64) echo "rid=linux-musl-x64" >> "$GITHUB_OUTPUT" ;;
darwin-x64) echo "rid=osx-x64" >> "$GITHUB_OUTPUT" ;;
darwin-arm64) echo "rid=osx-arm64" >> "$GITHUB_OUTPUT" ;;
esac
- name: Publish server (framework-dependent, R2R)
run: dotnet publish src/Heddle.LanguageServer/Heddle.LanguageServer.csproj -c Release -r ${{ steps.rid.outputs.rid }} --self-contained false -o editors/vscode/server -p:EnableToolR2R=true
- name: Package VSIX
working-directory: editors/vscode
run: |
npm ci
npm run copy-grammar
npx @vscode/vsce package --target ${{ matrix.target }}
- uses: actions/upload-artifact@v7
with:
name: heddle-vsix-${{ matrix.target }}
path: editors/vscode/*.vsix
8 changes: 5 additions & 3 deletions .github/workflows/npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@ jobs:
steps:
- uses: actions/checkout@v7
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v6
with:
node-version: 24
registry-url: https://registry.npmjs.org
scope: '@multiarc'
- name: Ensure npm supports staged/trusted publishing
run: npm install -g npm@latest # staged publishing requires npm >= 11.15.0
# Note: we deliberately do NOT upgrade npm here. The node-bundled npm ships its
# dependencies (including `sigstore`, used by the provenance path) intact; a global
# `npm install -g npm@latest` self-upgrade prunes those bundled deps and breaks
# provenance publishing with "Cannot find module 'sigstore'".
- name: Build custom Ace bundle
working-directory: src/Heddle.Language
run: bash build_ace.sh
Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/samples.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Integration demo gallery: every sample is a CI job with golden assertions.
# A red job here is an integration regression in the owning phase's feature.
name: Heddle Samples

on:
push:
branches: [ "main" ]
paths:
- 'samples/**'
- 'src/**'
- '.github/workflows/samples.yml'
pull_request:
branches: [ "main" ]
paths: # identical list, repeated verbatim — path filters
- 'samples/**' # are per-trigger; keep both lists in sync
- 'src/**'
- '.github/workflows/samples.yml'

permissions:
contents: read

jobs:
sample:
strategy:
fail-fast: false # one broken sample must not mask the others
matrix:
sample:
- ssr-aspnetcore
- definition-library
- dynamic-models
- sandboxed-user-templates
- html-safe-output
- custom-extensions
- component-props-slots
- codegen-t4-successor
- precompiled-app
- streaming-ssr
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x
- name: Comparer self-test
run: bash samples/tools/compare-golden-selftest.sh
- name: Run sample (capture mode)
run: dotnet run --project samples/${{ matrix.sample }} -c Release -- --capture out
- name: Compare against goldens
run: bash samples/tools/compare-golden.sh samples/${{ matrix.sample }}
Loading
Loading