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
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"rollForward": false
},
"purview.build": {
"version": "0.9.0",
"version": "0.9.1",
"commands": [
"purview-build"
],
Expand Down
38 changes: 38 additions & 0 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,55 @@ jobs:
name: Build and test
uses: purview-dev/build/.github/workflows/purview-build.yml@main
with:
# Pinned to the version in .config/dotnet-tools.json, so CI runs exactly what `just` runs locally
# instead of whichever Purview.Build was latest when the job started (which warns on every run).
build-version: "0.9.0"
# 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"
# Explicit, so PrWorkflow_GivenTheTestGates_RunsEveryTestInBothConfigurations can prove the
# Release gate still runs tests; the workflow default is true, but a default is invisible here.
run-tests: true
run-pack: true
validate-pack: true
secrets: inherit

# The shared pipeline runs a single configuration (Release, the tool's default). A failure that only
# reproduces in Debug would therefore reach a developer's `just test` (which defaults to Debug) and
# never CI. The cross-platform job below also runs Debug, but filters to the parity producers; this
# job builds the whole solution and runs every test in Debug.
debug:
name: Build and test (Debug)
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

# Repository.UnitTests drives the vitest suites from the .NET test run, so the JavaScript
# dependencies have to be installed first.
- uses: oven-sh/setup-bun@v2
with:
bun-version-file: package.json

- run: bun install --frozen-lockfile

- name: Build the solution (Debug)
run: dotnet build src/ZodSharp.slnx --configuration Debug

- name: Run every test (Debug)
run: >-
dotnet test --solution src/ZodSharp.slnx --configuration Debug
--no-restore --no-build --treenode-filter '/*/*/*/*'

# The shared pipeline runs the C# suite only, so the TypeScript/Zod side of the cross-platform
# parity guarantee (docs/wiki/quality/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
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ jobs:
# file name the policy is registered against.
- uses: purview-dev/build/actions/purview-release@main
with:
# Pinned to the version in .config/dotnet-tools.json, so the release runs exactly what the
# pull request validated instead of whichever Purview.Build was latest when the job started.
build-version: "0.9.0"
release-mode: NuGet
trusted-publishing: true
# The nuget.org profile name, not the sign-in email.
Expand Down
4 changes: 2 additions & 2 deletions docs/wiki/development/Release-Flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ rather than passing.

| Gate | What it proves | Local | CI |
|---|---|---|---|
| Build, lint and tests | The solution builds warning-free, CSharpier reports no change, and every test project passes (including the cross-platform vitest suites, through `Repository.UnitTests`). | `just build`, `just lint-check`, `just test` | `pr.yml` `build` |
| Build, lint and tests | The solution builds warning-free, CSharpier reports no change, and every test project passes (including the cross-platform vitest suites, through `Repository.UnitTests`). | `just build`, `just lint-check`, `just test` | `pr.yml` `build` (Release, with lint and pack) and `pr.yml` `debug` (the whole solution and every test in Debug) |
| Pack and pack validation | Every package packs; package validation holds the public API to the 2.4.2 baseline; `.config/purview-build.json` `PackValidation` finds exactly the declared content (`RequiredContent`, `RequireExplicitContent`) and none of the `ForbiddenContent`. | `just pipeline-pack-validate` | `pr.yml` `build` (`run-pack`, `validate-pack`) |
| Forbidden-content rule proof | Every `ForbiddenContent` rule rejects a package: a matching file is injected into scratch copies of each package and the real `purview-build` validation must fail, per package and per rule. | `just verify-pack-rules` | `pr.yml` `packed-consumers` |
| Packed generator | The IL-merged generator in the `.nupkg` loads and generates in a net10.0 consumer (it has regressed twice while the in-repo tests, which use the unmerged generator, stayed green). | `just smoke-packed-generator` | `pr.yml` `packed-generator` |
Expand Down Expand Up @@ -74,7 +74,7 @@ just publish-aot-sample # Native AOT minimal API sample an

| Workflow | Trigger | Pipeline mode |
|---|---|---|
| `pr.yml` | pull requests against `main` | `purview-build.yml` with `run-pack: true`, `validate-pack: true` |
| `pr.yml` | pull requests against `main` | `purview-build.yml` with `run-pack: true`, `validate-pack: true` (Release), plus a Debug build and full test run |
| `release.yml` | pushes to `main` | `purview-release.yml` with `release-mode: NuGet` |

Both consume `.config/purview-build.json`:
Expand Down
11 changes: 8 additions & 3 deletions src/tests/Blazor.UnitTests/GeneratedSchemaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,14 @@ public async Task DataAnnotationsValidator_GivenAsyncOnlyMemberFieldChange_IsPen
var form = context.Render(editContext, FormHost.DataAnnotations());
FieldIdentifier field = new(cat, nameof(GeneratedCat.Name));

// Act
await form.ChangeAsync(editContext, field);
var pending = editContext.IsValidationPending(field);
// Act - read the pending state inside the renderer's dispatch operation. The validation suspends at its
// first await, and its continuation is queued on that same dispatcher, so sampling after the call returns
// races the completion (a Release run on a busy Linux runner resolves before the test thread resumes).
var pending = await form.InvokeAsync(() =>
{
editContext.NotifyFieldChanged(field);
return editContext.IsValidationPending(field);
});
await FormHost.WaitUntilAsync(() => !editContext.IsValidationPending(field), "resolved");

// Assert
Expand Down
28 changes: 28 additions & 0 deletions src/tests/Repository.UnitTests/ReleaseGateTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,34 @@ CancellationToken cancellationToken
await Assert.That(job).Contains("dotnet workload install wasm-tools");
}

/// <summary>
/// The shared pipeline runs a single configuration, so without the Debug job CI would only ever exercise
/// Release while `just test` runs Debug locally. A test that fails only in Debug would reach a developer and
/// never the pull request. Both configurations must build the solution and run every test.
/// </summary>
[Test]
public async Task PrWorkflow_GivenTheTestGates_RunsEveryTestInBothConfigurations(
CancellationToken cancellationToken
)
{
// Arrange
var text = await File.ReadAllTextAsync(
Path.Combine(RepositoryRoot.Value, ".github", "workflows", "pr.yml"),
cancellationToken
);

// Act
var jobs = Jobs(text).ToArray();
var release = jobs.SingleOrDefault(static j => j.Name == "build").Body ?? string.Empty;
var debug = jobs.SingleOrDefault(static j => j.Name == "debug").Body ?? string.Empty;

// Assert
await Assert.That(release).Contains("run-tests: true");
await Assert.That(debug).Contains("dotnet build src/ZodSharp.slnx --configuration Debug");
await Assert.That(debug).Contains("dotnet test --solution src/ZodSharp.slnx --configuration Debug");
await Assert.That(debug).Contains("--treenode-filter '/*/*/*/*'");
}

/// <summary>
/// Compiler-host assemblies must stay forbidden everywhere NuGet hands files to an application, and the generator
/// and code fixes everywhere an application loads assemblies from. <c>scripts/test-pack-validation-rules.ps1</c>
Expand Down
Loading