Repository navigation
Net11 extensions #74
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: PR | |
| on: | |
| pull_request: | |
| branches: [main] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| build: | |
| name: Build and test | |
| uses: purview-dev/build/.github/workflows/purview-build.yml@main | |
| with: | |
| # The shared workflow defaults to the 10.0.x SDK, but this repository targets net11.0 and pins | |
| # the .NET 11 SDK in global.json. Without the matching SDK the runner cannot resolve global.json | |
| # ("A compatible .NET SDK was not found") and every step fails, starting with `dotnet tool | |
| # install`. Before global.json pinned a version the same gap failed later instead, with | |
| # NETSDK1045 on the net11.0 target, because the 10.0.x SDK cannot build it. | |
| # Keep in sync with `sdk.version` in global.json. | |
| dotnet-version: "11.0.100-rc.3.26522.148" | |
| run-pack: true | |
| validate-pack: true | |
| secrets: inherit | |
| # The shared pipeline runs the C# suite only, so the TypeScript/Zod side of the cross-platform | |
| # parity guarantee (docs/wiki/Guarantees-and-Limitations.md) would otherwise never run in CI. | |
| # The vitest suite validates the JSON the C# cross-platform tests write, so the C# tests must run | |
| # first, in this same workspace — hence running them here rather than relying on the `build` job. | |
| cross-platform: | |
| name: Cross-platform (C# to Zod) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # Installs exactly the SDK global.json pins, which is the only thing the first dotnet command | |
| # in the job can resolve against. Naming a floating version and a feed quality instead does | |
| # not work: `11.0.x` with quality `preview` resolves the latest *preview*-channel build, and a | |
| # preview sorts below the pinned rc, so global.json is left unsatisfiable. | |
| - uses: actions/setup-dotnet@v6 | |
| with: | |
| global-json-file: global.json | |
| - uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version-file: package.json | |
| - run: bun install --frozen-lockfile | |
| # There are FOUR producers of the fixtures vitest reads, in different assemblies, namespaces and | |
| # classes, so they cannot be selected by one treenode filter: | |
| # | |
| # tests/ts/cross-platform.test.ts <- src/tests/cross-platform/output/ | |
| # ZodSharp.Json/*CrossPlatformTests (SystemTextJson and | |
| # NewtonsoftJson unit tests) | |
| # tests/ts/guid-uuid-parity.test.ts <- src/tests/cross-platform/guid-parity/ | |
| # ZodSharp.SourceGenerators/ZodSchemaGeneratorTests | |
| # tests/ts/temporal-parity.test.ts <- src/tests/cross-platform/temporal-parity/ | |
| # ZodSharp.SourceGenerators/ZodSchemaGeneratorTests | |
| # tests/ts/client-validators.test.ts <- src/tests/cross-platform/client-rules/ | |
| # ZodSharp.Blazor.Ssr/ClientRuleParityFixtureTests | |
| # | |
| # They run as separate steps rather than one OR-ed filter: `a|b` is not honoured as a top-level | |
| # alternation by the treenode filter — it silently selects a different, broader set and skips a | |
| # parity producer, which is how the parity artifacts came to be missing in CI while this job stayed | |
| # green. Keep a step per producer, and keep each filter anchored to its class. | |
| - name: Run the C# cross-platform tests (writes cross-platform/output) | |
| run: >- | |
| dotnet test --solution src/ZodSharp.slnx --configuration Debug | |
| --treenode-filter '/*/ZodSharp.Json/*CrossPlatformTests/*' | |
| - name: Run the C# Guid/UUID parity test (writes cross-platform/guid-parity) | |
| run: >- | |
| dotnet test --solution src/ZodSharp.slnx --configuration Debug | |
| --treenode-filter | |
| '/*/ZodSharp.SourceGenerators/ZodSchemaGeneratorTests/JsonSchema_GivenGeneratedGuidSchemas_WritesZodParityArtifacts' | |
| - name: Run the C# temporal parity test (writes cross-platform/temporal-parity) | |
| run: >- | |
| dotnet test --solution src/ZodSharp.slnx --configuration Debug | |
| --treenode-filter | |
| '/*/ZodSharp.SourceGenerators/ZodSchemaGeneratorTests/JsonSchema_GivenGeneratedTemporalSchemas_WritesZodParityArtifacts' | |
| - name: Run the C# client-validator parity test (writes cross-platform/client-rules) | |
| run: >- | |
| dotnet test --solution src/ZodSharp.slnx --configuration Debug | |
| --treenode-filter | |
| '/*/ZodSharp.Blazor.Ssr/ClientRuleParityFixtureTests/ClientRules_GivenEveryMappedRule_WritesParityFixtures' | |
| - name: Run the TypeScript/Zod cross-platform tests | |
| run: bun run test | |
| # The only reliable proof that the minimal-API integration is trim- and Native AOT-safe end to end | |
| # (generated registration, the Microsoft.Extensions.Validation bridge, ProblemDetails and the OpenAPI | |
| # transformers): publish the sample with PublishAot=true, fail on any IL2xxx/IL3xxx warning, then run the | |
| # native binary and check one failing request still returns the ProblemDetails contract. The analyzers that | |
| # run during the normal build only see each assembly on its own; ILC sees the whole application. | |
| aot-sample: | |
| name: Native AOT sample (publish and smoke) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # Same reason as the cross-platform job: the SDK comes from global.json, not a floating | |
| # version plus a feed quality. | |
| - uses: actions/setup-dotnet@v6 | |
| with: | |
| global-json-file: global.json | |
| # Native AOT links with clang and needs the zlib headers; the hosted image may not carry both. | |
| - name: Install the Native AOT prerequisites | |
| run: sudo apt-get update && sudo apt-get install -y --no-install-recommends clang zlib1g-dev | |
| # -warnaserror turns every warning ILC reports (IL2xxx trim, IL3xxx AOT) into an error, so the publish | |
| # itself fails. The grep is a second gate in case a warning is ever logged without failing the step. | |
| - name: Publish Examples.MinimalApi with Native AOT (fails on any trim or AOT warning) | |
| shell: bash | |
| run: | | |
| dotnet publish src/src/Examples.MinimalApi/Examples.MinimalApi.csproj \ | |
| --configuration Release --runtime linux-x64 --output artifacts/aot-sample \ | |
| -warnaserror 2>&1 | tee publish.log | |
| if grep -E 'warning IL[23][0-9]{3}' publish.log; then | |
| echo "::error::The Native AOT publish reported trim or AOT warnings." | |
| exit 1 | |
| fi | |
| test -x artifacts/aot-sample/ZodSharp.Examples.MinimalApi | |
| # Compiler-host assemblies (the generator, the code fixes, Purview.SourceGeneratorFramework, Roslyn) must never | |
| # reach build or publish output: Purview.SourceGeneratorFramework.dll once did, through analyzer-only references, | |
| # and broke Blazor WebAssembly AOT. Scans file names and every deps/runtimeconfig/boot manifest. | |
| - name: Scan the build and publish output for compiler-host assemblies | |
| shell: pwsh | |
| run: >- | |
| ./scripts/assert-no-compiler-host-assemblies.ps1 | |
| -Path artifacts/aot-sample, src/src/Examples.MinimalApi/bin/Release | |
| - name: Run the native binary and check a failing request returns ProblemDetails | |
| shell: bash | |
| working-directory: artifacts/aot-sample | |
| run: | | |
| ASPNETCORE_URLS=http://127.0.0.1:5080 ./ZodSharp.Examples.MinimalApi > app.log 2>&1 & | |
| app_pid=$! | |
| trap 'kill "$app_pid" 2>/dev/null || true' EXIT | |
| for _ in $(seq 1 30); do | |
| curl --silent --fail --output /dev/null http://127.0.0.1:5080/openapi/v1.json && break | |
| sleep 1 | |
| done | |
| result=$(curl --silent --show-error --output body.json \ | |
| --write-out '%{http_code} %{content_type}' \ | |
| --request POST http://127.0.0.1:5080/orders \ | |
| --header 'Content-Type: application/json' \ | |
| --data '{"customerEmail":"not-an-email","lines":[{"sku":"abc","quantity":0}]}') | |
| echo "$result" | |
| cat body.json; echo | |
| if [[ "$result" != "400 application/problem+json"* ]] || ! grep -q '"issues"' body.json; then | |
| echo "::error::Expected a 400 application/problem+json response with ZodSharp issues." | |
| cat app.log | |
| exit 1 | |
| fi | |
| # The only reliable proof that the Blazor integration is trim-safe in the browser: publish the WebAssembly half of | |
| # the Blazor sample (Examples.Blazor.Client - ZodValidator, ZodValidationMessage, AddZodProblemDetails, the generated | |
| # registration and the service-aware async rule) trimmed, and fail on any IL2xxx/IL3xxx warning. Then hold the | |
| # Brotli-compressed size of every Purview.ZodSharp assembly in the output to .config/wasm-size-baseline.json (+10%). | |
| # The RunAOTCompilation publish and the browser test are slower and run nightly (wasm-nightly.yml). `just wasm-size` | |
| # runs the same publish and check locally. | |
| wasm-trimmed: | |
| name: Blazor WebAssembly trimmed publish and size budget | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # Same reason as the cross-platform job: the SDK comes from global.json, not a floating | |
| # version plus a feed quality. | |
| - uses: actions/setup-dotnet@v6 | |
| with: | |
| global-json-file: global.json | |
| # -warnaserror fails the publish on any trim warning ILLink reports. The grep is a second gate in case a warning | |
| # is ever logged without failing the step. | |
| - name: Publish Examples.Blazor.Client trimmed (fails on any trim or AOT warning) | |
| shell: bash | |
| run: | | |
| dotnet publish src/src/Examples.Blazor.Client/Examples.Blazor.Client.csproj \ | |
| --configuration Release --output artifacts/wasm-trimmed \ | |
| -warnaserror 2>&1 | tee publish.log | |
| if grep -E 'warning IL[23][0-9]{3}' publish.log; then | |
| echo "::error::The trimmed WebAssembly publish reported trim or AOT warnings." | |
| exit 1 | |
| fi | |
| - name: Check the Purview.ZodSharp size budget | |
| shell: pwsh | |
| run: ./scripts/check-wasm-size.ps1 -PublishDirectory artifacts/wasm-trimmed | |
| # Compiler-host assemblies (the generator, the code fixes, Purview.SourceGeneratorFramework, Roslyn) must never | |
| # reach build or publish output: Purview.SourceGeneratorFramework.dll once did, through analyzer-only references, | |
| # and broke Blazor WebAssembly AOT. Scans file names and every deps/runtimeconfig/boot manifest. | |
| - name: Scan the build and publish output for compiler-host assemblies | |
| shell: pwsh | |
| run: >- | |
| ./scripts/assert-no-compiler-host-assemblies.ps1 | |
| -Path artifacts/wasm-trimmed, src/src/Examples.Blazor.Client/bin/Release | |
| # Proves the whole trim-safe surface, not just what one sample calls. The harness publishes with PublishAot=true twice: | |
| # once with every Purview.ZodSharp assembly except NewtonsoftJson rooted whole, so ILC analyses all of their code, and | |
| # once as a plain consumer. Any IL2xxx/IL3xxx warning fails the publish, and each native binary exercises runtime | |
| # schemas, generated validators, System.Text.Json, JSON Schema, the validation bridge, ProblemDetails and options | |
| # validation, exiting non-zero on any mismatch. See src/tests/TrimSafety.Verification. | |
| verify-trim: | |
| name: Trim and Native AOT verification | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # Same reason as the cross-platform job: the SDK comes from global.json, not a floating | |
| # version plus a feed quality. | |
| - uses: actions/setup-dotnet@v6 | |
| with: | |
| global-json-file: global.json | |
| # Native AOT links with clang and needs the zlib headers; the hosted image may not carry both. | |
| - name: Install the Native AOT prerequisites | |
| run: sudo apt-get update && sudo apt-get install -y --no-install-recommends clang zlib1g-dev | |
| # -warnaserror and the project's TreatWarningsAsErrors fail the publish on any trim or AOT warning. The grep is a | |
| # second gate in case a warning is ever logged without failing the step. | |
| - name: Publish and run the harness (every ZodSharp assembly rooted) | |
| shell: bash | |
| run: | | |
| dotnet publish src/tests/TrimSafety.Verification/TrimSafety.Verification.csproj \ | |
| --configuration Release --runtime linux-x64 --output artifacts/trim-verification/rooted \ | |
| -p:TrimVerificationMode=Rooted -warnaserror 2>&1 | tee publish-rooted.log | |
| if grep -E 'warning IL[23][0-9]{3}' publish-rooted.log; then | |
| echo "::error::The rooted Native AOT publish reported trim or AOT warnings." | |
| exit 1 | |
| fi | |
| ./artifacts/trim-verification/rooted/ZodSharp.TrimSafety.Verification | |
| - name: Publish and run the harness (as a consumer) | |
| shell: bash | |
| run: | | |
| dotnet publish src/tests/TrimSafety.Verification/TrimSafety.Verification.csproj \ | |
| --configuration Release --runtime linux-x64 --output artifacts/trim-verification/consumer \ | |
| -p:TrimVerificationMode=Consumer -warnaserror 2>&1 | tee publish-consumer.log | |
| if grep -E 'warning IL[23][0-9]{3}' publish-consumer.log; then | |
| echo "::error::The consumer Native AOT publish reported trim or AOT warnings." | |
| exit 1 | |
| fi | |
| ./artifacts/trim-verification/consumer/ZodSharp.TrimSafety.Verification | |
| # Compiler-host assemblies (the generator, the code fixes, Purview.SourceGeneratorFramework, Roslyn) must never | |
| # reach build or publish output: Purview.SourceGeneratorFramework.dll once did, through analyzer-only references, | |
| # and broke Blazor WebAssembly AOT. Scans file names and every deps/runtimeconfig/boot manifest. | |
| - name: Scan the build and publish output for compiler-host assemblies | |
| shell: pwsh | |
| run: >- | |
| ./scripts/assert-no-compiler-host-assemblies.ps1 | |
| -Path artifacts/trim-verification/rooted, artifacts/trim-verification/consumer, | |
| src/tests/TrimSafety.Verification/bin/Release | |
| # Pack validation checks the IL-merged generator is present in the .nupkg; it cannot check that the | |
| # assembly loads and generates. That merge has regressed twice (#36, #38), both times breaking consumers | |
| # while the in-repo tests stayed green, because those run against the unmerged generator. | |
| packed-generator: | |
| name: Packed generator smoke test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # Same reason as the cross-platform job: the SDK comes from global.json, not a floating | |
| # version plus a feed quality. | |
| - uses: actions/setup-dotnet@v6 | |
| with: | |
| global-json-file: global.json | |
| - name: Build and run a consumer against the packed generator | |
| shell: pwsh | |
| run: ./scripts/test-packed-generator.ps1 -Configuration Release | |
| # Every other publish job builds against ProjectReferences; this one builds what ships. It packs every shipping | |
| # package to an isolated feed and package cache, then publishes and runs throwaway consumers that reference the | |
| # packages only - a Native AOT console, a Blazor WebAssembly app (trimmed and RunAOTCompilation) and a Native AOT | |
| # minimal API - and leak-scans every bin and publish folder for compiler-host assemblies. It then proves every | |
| # PackValidation ForbiddenContent rule rejects a package, against the packages the consumers used. | |
| # `just smoke-packed-consumers` and `just verify-pack-rules` run the same locally. See docs/wiki/Release-Flow.md. | |
| packed-consumers: | |
| name: Packed consumers (Native AOT, Blazor WebAssembly, minimal API) | |
| runs-on: ubuntu-latest | |
| env: | |
| # An extra feed for Purview.SourceGeneratorFramework only, for a version that is not on nuget.org yet. Unset, the | |
| # gate restores it from nuget.org alone. | |
| ZODSHARP_SGF_FEED: ${{ vars.ZODSHARP_SGF_FEED }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # Same reason as the cross-platform job: the SDK comes from global.json, not a floating | |
| # version plus a feed quality. | |
| - uses: actions/setup-dotnet@v6 | |
| with: | |
| global-json-file: global.json | |
| # First, before any toolchain is installed: the generator is built against the Purview.SourceGeneratorFramework | |
| # version Directory.Packages.props pins, so nothing can be packed until that version restores. This fails with | |
| # what to do (publish it to nuget.org, or set the ZODSHARP_SGF_FEED repository variable) rather than skipping. | |
| - name: Check Purview.SourceGeneratorFramework restores | |
| shell: pwsh | |
| run: ./scripts/test-packed-consumers.ps1 -PreflightOnly | |
| # Native AOT links with clang and needs the zlib headers; the hosted image may not carry both. | |
| - name: Install the Native AOT prerequisites | |
| run: sudo apt-get update && sudo apt-get install -y --no-install-recommends clang zlib1g-dev | |
| # RunAOTCompilation needs the Emscripten toolchain and the Mono AOT cross compiler. -WasmAot Require below makes a | |
| # missing workload an error instead of a skipped step. | |
| - name: Install the wasm-tools workload | |
| run: dotnet workload install wasm-tools | |
| - name: Pack, then publish, run and leak-scan consumers of the packages | |
| shell: pwsh | |
| run: >- | |
| ./scripts/test-packed-consumers.ps1 -RuntimeIdentifier linux-x64 -WasmAot Require | |
| -PackOutput artifacts/packed | |
| - name: Prove every pack-validation forbidden-content rule rejects a package | |
| shell: pwsh | |
| run: | | |
| dotnet tool restore | |
| ./scripts/test-pack-validation-rules.ps1 -PackagesFolder artifacts/packed |