From beb5aef18816526d47478886ce4f4f858e054d6a Mon Sep 17 00:00:00 2001 From: Rico Suter Date: Wed, 22 Apr 2026 00:07:27 +0200 Subject: [PATCH 1/5] Set up v15 integration branch: plan doc, changelog, CI triggers, sibling NJsonSchema checkout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add docs/plan_v15.md describing the v15 scope, dependency on NJsonSchema v12, development workflow (UseLocalNJsonSchemaProjects=true), branch model, release plan, CI setup, and pre-release cleanup checklist - Add docs/changelog_v15.md as the running landed-changes log and v14 → v15 migration guide (starts empty with structural placeholders for planned PR #5355 and NJsonSchema v12 cascaded changes) - Add v15 to OnPullRequestBranches / OnPushBranches in build/Build.CI.GitHubActions.cs and the regenerated workflow YAMLs - Hand-edit both workflow YAMLs to clone RicoSuter/NJsonSchema@v12 as a sibling directory before build, so project references in UseLocalNJsonSchemaProjects=true mode resolve on CI (the clone step will be reverted when NUKE regenerates the YAMLs at v15 → master merge time; see cleanup checklist in plan_v15.md) --- .github/workflows/build.yml | 7 +++ .github/workflows/pr.yml | 7 +++ build/Build.CI.GitHubActions.cs | 4 +- docs/changelog_v15.md | 62 +++++++++++++++++++++++ docs/plan_v15.md | 90 +++++++++++++++++++++++++++++++++ 5 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 docs/changelog_v15.md create mode 100644 docs/plan_v15.md diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0242d0da7..95b24f748 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,6 +21,7 @@ on: branches: - master - main + - v15 tags: - 'v*.*.*' paths: @@ -45,6 +46,8 @@ jobs: dotnet-version: | 10.0 - uses: actions/checkout@v6 + - name: 'Clone NJsonSchema (v12) as sibling for project references' + run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema - name: 'Run: Compile, Test, Pack, Publish' run: ./build.cmd Compile Test Pack Publish env: @@ -81,6 +84,8 @@ jobs: dotnet-version: | 10.0 - uses: actions/checkout@v6 + - name: 'Clone NJsonSchema (v12) as sibling for project references' + run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema - name: 'Run: Compile, Test, Pack, Publish' run: ./build.cmd Compile Test Pack Publish env: @@ -97,6 +102,8 @@ jobs: dotnet-version: | 10.0 - uses: actions/checkout@v6 + - name: 'Clone NJsonSchema (v12) as sibling for project references' + run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema - name: 'Run: Compile, Test, Pack, Publish' run: ./build.cmd Compile Test Pack Publish env: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index fbd310904..8a31e358a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -21,6 +21,7 @@ on: branches: - master - main + - v15 paths: - '**/*.*' - '!**/*.md' @@ -47,6 +48,8 @@ jobs: dotnet-version: | 10.0 - uses: actions/checkout@v6 + - name: 'Clone NJsonSchema (v12) as sibling for project references' + run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema - name: 'Run: Compile, Test, Pack' run: ./build.cmd Compile Test Pack - name: 'Publish: NSwag.zip' @@ -78,6 +81,8 @@ jobs: dotnet-version: | 10.0 - uses: actions/checkout@v6 + - name: 'Clone NJsonSchema (v12) as sibling for project references' + run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema - name: 'Run: Compile, Test, Pack' run: ./build.cmd Compile Test Pack macos-latest: @@ -89,5 +94,7 @@ jobs: dotnet-version: | 10.0 - uses: actions/checkout@v6 + - name: 'Clone NJsonSchema (v12) as sibling for project references' + run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema - name: 'Run: Compile, Test, Pack' run: ./build.cmd Compile Test Pack diff --git a/build/Build.CI.GitHubActions.cs b/build/Build.CI.GitHubActions.cs index 7c270aa6e..18a152a44 100644 --- a/build/Build.CI.GitHubActions.cs +++ b/build/Build.CI.GitHubActions.cs @@ -10,7 +10,7 @@ GitHubActionsImage.WindowsLatest, GitHubActionsImage.UbuntuLatest, GitHubActionsImage.MacOsLatest, - OnPullRequestBranches = ["master", "main"], + OnPullRequestBranches = ["master", "main", "v15"], OnPullRequestIncludePaths = ["**/*.*"], OnPullRequestExcludePaths = ["**/*.md"], PublishArtifacts = true, @@ -23,7 +23,7 @@ GitHubActionsImage.WindowsLatest, GitHubActionsImage.UbuntuLatest, GitHubActionsImage.MacOsLatest, - OnPushBranches = ["master", "main"], + OnPushBranches = ["master", "main", "v15"], OnPushTags = ["v*.*.*"], OnPushIncludePaths = ["**/*.*"], OnPushExcludePaths = ["**/*.md"], diff --git a/docs/changelog_v15.md b/docs/changelog_v15.md new file mode 100644 index 000000000..cbf513711 --- /dev/null +++ b/docs/changelog_v15.md @@ -0,0 +1,62 @@ +# NSwag v15 Changelog + +Running record of changes on the `v15` branch and migration guidance for users upgrading from v14. + +See [`plan_v15.md`](./plan_v15.md) for the v15 scope, branch model, and release plan. + +--- + +## Unreleased + +### Breaking changes + +- **Set up v15 integration branch, CI triggers, and sibling NJsonSchema v12 checkout** (this PR) — no user-facing impact; infrastructure only. + +Planned (not yet merged — track via linked PRs): + +- **STJ core migration** — PR [#5355](https://github.com/RicoSuter/NSwag/pull/5355). Mirrors NJsonSchema v12's STJ migration. +- **Absorb NJsonSchema v12 breaking API changes** — `ValidationError.Token` type change, `SchemaType` enum expansion (OpenAPI 3.0 vs 3.1), reference-resolution semantics, etc. Items added as upstream changes land. + +### New features + +*(to be filled as PRs merge)* + +### Fixes + +*(to be filled as PRs merge)* + +--- + +## Migration guide (v14 → v15) + +Intended as a running "how do I upgrade" companion. Each section is added as breaking changes land on the `v15` branch. + +### System.Text.Json migration + +*(placeholder — to be filled when PR #5355 merges)* + +- What changes for NSwag consumers +- Before/after code snippets +- Newtonsoft.Json escape hatch if you need to keep the old behavior + +### NJsonSchema v12 dependency + +*(placeholder — to be filled closer to release)* + +- NSwag v15 requires NJsonSchema v12.0.0 or later. +- Breaking changes that cascade from NJsonSchema v12 — link to [NJsonSchema's migration guide](https://github.com/RicoSuter/NJsonSchema/blob/v12/docs/changelog_v12.md#migration-guide-v11--v12). + +### `SchemaType` enum expansion (OpenAPI 3.0 vs 3.1) + +*(placeholder — to be filled when the upstream NJsonSchema enum change lands and NSwag absorbs it)* + +--- + +## Contributing + +When merging a v15 PR that includes a user-visible change: + +1. Add an entry under `Unreleased → Breaking changes` / `New features` / `Fixes`. +2. If it breaks v14 consumers, also add a section under **Migration guide** with a before/after example. +3. If the change cascades from an NJsonSchema v12 change, link to the corresponding entry in NJsonSchema's `changelog_v12.md` migration guide. +4. Keep entries concise; link to the merged PR for full detail. diff --git a/docs/plan_v15.md b/docs/plan_v15.md new file mode 100644 index 000000000..7a3839218 --- /dev/null +++ b/docs/plan_v15.md @@ -0,0 +1,90 @@ +# NSwag v15 + +Scope, branch model, release plan, and running changelog for the v15 major release. + +## Scope + +v15 is gated on NJsonSchema v12 and bundles the companion set of breaking improvements: + +- **STJ core migration** (PR [#5355](https://github.com/RicoSuter/NSwag/pull/5355)) — mirrors NJsonSchema v12's STJ migration. Newtonsoft.Json usage in `NSwag.Core` and siblings moves to Newtonsoft-specific packages where possible. +- **Absorb NJsonSchema v12 breaking API changes** — `ValidationError.Token` type change, `SchemaType` enum expansion (3.0 vs 3.1), reference-resolution fixes, etc. +- **Other v15 cleanups** as identified during development (rename-risk members, deprecated API removal). + +## Dependency on NJsonSchema v12 + +NSwag v15 depends on NJsonSchema v12. During Phase 1 development, this is wired via **project references** using the `UseLocalNJsonSchemaProjects=true` MSBuild property (default true on the `v15` branch). Practical implications: + +- Both repos are checked out as siblings locally: `../NJsonSchema` on the `v12` branch, `../NSwag` on the `v15` branch. +- Changes to NJsonSchema v12 flow into the local NSwag v15 build immediately. +- A breaking change on NJsonSchema v12 that NSwag v15 relies on surfaces as a compile error on the very next build — the "cross-check with NSwag" rule from [`CLAUDE.md`](../CLAUDE.md) is mechanically enforced. + +When NJsonSchema v12 is feature-complete (Phase 2), NSwag v15 flips to `UseLocalNJsonSchemaProjects=false` and consumes NJsonSchema v12 preview NuGet packages for external integration validation before either library releases. + +## CI setup (temporary) + +While v15 uses `UseLocalNJsonSchemaProjects=true`, the `v15` branch's CI workflows check out `RicoSuter/NJsonSchema` at the `v12` branch as a sibling directory: + +```yaml +- uses: actions/checkout@v6 +- name: 'Clone NJsonSchema (v12) as sibling for project references' + run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema +``` + +Layout on the runner: `$GITHUB_WORKSPACE/../NJsonSchema` (sibling to the NSwag checkout), which matches the relative project-reference paths (`../../../NJsonSchema/src/…`). + +**Note:** The NSwag workflow YAMLs are normally auto-generated by NUKE (`build/Build.CI.GitHubActions.cs`). The sibling-clone step is a hand edit on the `v15` branch only; NUKE regeneration would remove it. This is intentional — see the cleanup checklist below. + +## Branch model + +Mirrors NJsonSchema v12. The `v15` branch is the integration branch. + +``` +master (v14.x stable) ────o────o────o────o────o────o──── v14.x patches + \ \ + \ periodic merges \ + ↓ ↓ +v15 (integration) ─────o────o────O────O────O────O────── release v15.0.0 → merge to master + ↑ + feature PRs target v15 +``` + +Rules: + +- `master` remains the v14.x stable line. +- All v15 feature PRs target `v15`. +- `master` is merged into `v15` periodically to keep v15 fresh. +- When v15 is ready, it is merged into `master` via a final PR (`--no-ff`), tagged `v15.0.0`, and published. + +## Release plan + +Ordered by the NJsonSchema → NSwag dependency: + +1. **NJsonSchema v12** stabilizes, tagged `v12.0.0`, published to NuGet. +2. **NSwag v15** bumps its dependency to `NJsonSchema 12.0.0`, switches `UseLocalNJsonSchemaProjects=false`, stabilizes, is tagged `v15.0.0`, and published. + +v14.x patches continue to ship from `master` until superseded. + +## Pre-release cleanup checklist + +Before the final `v15` → `master` merge, revert the temporary shims that only make sense while v15 is a live branch: + +- **`build/Build.CI.GitHubActions.cs`**: remove `"v15"` from `OnPullRequestBranches` and `OnPushBranches`. +- **`.github/workflows/build.yml`, `.github/workflows/pr.yml`**: regenerate from NUKE (`nuke --generate-configuration GitHubActions_build --host GitHubActions` and the `pr` equivalent). This will: + - Remove the `v15` branch filter entry. + - **Remove the hand-added sibling NJsonSchema checkout steps** — they're the temporary glue for `UseLocalNJsonSchemaProjects=true` in CI and won't make sense once v15 references stable NJsonSchema v12 NuGet packages. +- **`Directory.Packages.props` (or equivalent)**: flip `UseLocalNJsonSchemaProjects` default back to `false` (or remove the property entirely if the conditional ItemGroups also go). NSwag `master` should consume NJsonSchema via NuGet only. +- **Bump NJsonSchema package references** to the released stable `12.0.0`. +- **`docs/plan_v15.md` and `docs/changelog_v15.md`**: move or archive after release — either delete once v15 is GA, or relocate to `docs/releases/` as historical context. +- **Any `[Obsolete]` shims** added specifically to ease the v14→v15 migration: remove or mark for removal in v16. + +Add items to this checklist as new temporary shims are introduced during v15 development so nothing leaks into the final release. + +## Contributing to v15 + +- Open PRs against `v15`, not `master`. +- If the change requires an NJsonSchema change, open the NJsonSchema PR first (against NJsonSchema's `v12` branch) and reference it from your NSwag PR so reviewers see the full picture. +- Non-breaking bug fixes unrelated to v15 scope should target `master` (v14.x) — they will be brought into `v15` via periodic master→v15 merges. + +## Changelog and migration guide + +See [`changelog_v15.md`](./changelog_v15.md) for the running list of landed changes and the v14 → v15 migration guide. Every PR merged to `v15` that has user-visible impact should update that file (see its `Contributing` section). From 7758cd7d678fc23e0b5f5ffa7d38be6c92ff7edf Mon Sep 17 00:00:00 2001 From: Rico Suter Date: Wed, 22 Apr 2026 00:20:28 +0200 Subject: [PATCH 2/5] Add UseLocalNJsonSchemaProjects mechanism for v15 dev Pulled from PR #5355 so the v15 integration branch can build against local NJsonSchema v12 source immediately, validating the sibling-checkout CI setup end-to-end rather than waiting for the STJ migration PR to land. - Directory.Build.props: new UseLocalNJsonSchemaProjects property, default true on v15 - 8 csprojs get a pair of conditional ItemGroup blocks (ProjectReference when UseLocalNJsonSchemaProjects=true, PackageReference otherwise): NSwag.Core, NSwag.Core.Yaml, NSwag.CodeGeneration, NSwag.CodeGeneration.CSharp, NSwag.CodeGeneration.TypeScript, NSwag.Generation, NSwag.AspNet.WebApi, NSwag.CodeGeneration.Tests Verified locally: NSwag.Core, NSwag.CodeGeneration.CSharp, and NSwag.Generation all build clean with ProjectReference resolving to ../NJsonSchema/src/... Pre-release cleanup updated in plan_v15.md and this change noted in changelog_v15.md. --- Directory.Build.props | 4 ++++ docs/changelog_v15.md | 4 +++- docs/plan_v15.md | 17 ++++++++++++++--- .../NSwag.AspNet.WebApi.csproj | 6 ++++++ .../NSwag.CodeGeneration.CSharp.csproj | 5 ++++- .../NSwag.CodeGeneration.Tests.csproj | 8 +++++++- .../NSwag.CodeGeneration.TypeScript.csproj | 5 ++++- .../NSwag.CodeGeneration.csproj | 5 ++++- src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj | 5 ++++- src/NSwag.Core/NSwag.Core.csproj | 5 ++++- src/NSwag.Generation/NSwag.Generation.csproj | 5 ++++- 11 files changed, 58 insertions(+), 11 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 8adba07b7..0ec889979 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -36,6 +36,10 @@ true + + true + diff --git a/docs/changelog_v15.md b/docs/changelog_v15.md index cbf513711..9e02d44b6 100644 --- a/docs/changelog_v15.md +++ b/docs/changelog_v15.md @@ -10,7 +10,9 @@ See [`plan_v15.md`](./plan_v15.md) for the v15 scope, branch model, and release ### Breaking changes -- **Set up v15 integration branch, CI triggers, and sibling NJsonSchema v12 checkout** (this PR) — no user-facing impact; infrastructure only. +- **Set up v15 integration branch, project-ref mechanism, CI triggers, and sibling NJsonSchema v12 checkout** (this PR) — no user-facing impact; infrastructure only. + - Adds `UseLocalNJsonSchemaProjects` property (`Directory.Build.props`, default `true` on v15). + - Converts 8 NSwag csprojs that reference NJsonSchema packages to conditional `ItemGroup` blocks: `NSwag.Core`, `NSwag.Core.Yaml`, `NSwag.CodeGeneration`, `NSwag.CodeGeneration.CSharp`, `NSwag.CodeGeneration.TypeScript`, `NSwag.Generation`, `NSwag.AspNet.WebApi`, `NSwag.CodeGeneration.Tests`. Planned (not yet merged — track via linked PRs): diff --git a/docs/plan_v15.md b/docs/plan_v15.md index 7a3839218..30412d993 100644 --- a/docs/plan_v15.md +++ b/docs/plan_v15.md @@ -12,7 +12,18 @@ v15 is gated on NJsonSchema v12 and bundles the companion set of breaking improv ## Dependency on NJsonSchema v12 -NSwag v15 depends on NJsonSchema v12. During Phase 1 development, this is wired via **project references** using the `UseLocalNJsonSchemaProjects=true` MSBuild property (default true on the `v15` branch). Practical implications: +NSwag v15 depends on NJsonSchema v12. During Phase 1 development, this is wired via **project references** using the `UseLocalNJsonSchemaProjects` MSBuild property, defined in `Directory.Build.props` and defaulting to `true` on the `v15` branch. Each NSwag csproj that depends on NJsonSchema has a pair of conditional `ItemGroup` blocks: + +```xml + + + + + + +``` + +Practical implications: - Both repos are checked out as siblings locally: `../NJsonSchema` on the `v12` branch, `../NSwag` on the `v15` branch. - Changes to NJsonSchema v12 flow into the local NSwag v15 build immediately. @@ -72,8 +83,8 @@ Before the final `v15` → `master` merge, revert the temporary shims that only - **`.github/workflows/build.yml`, `.github/workflows/pr.yml`**: regenerate from NUKE (`nuke --generate-configuration GitHubActions_build --host GitHubActions` and the `pr` equivalent). This will: - Remove the `v15` branch filter entry. - **Remove the hand-added sibling NJsonSchema checkout steps** — they're the temporary glue for `UseLocalNJsonSchemaProjects=true` in CI and won't make sense once v15 references stable NJsonSchema v12 NuGet packages. -- **`Directory.Packages.props` (or equivalent)**: flip `UseLocalNJsonSchemaProjects` default back to `false` (or remove the property entirely if the conditional ItemGroups also go). NSwag `master` should consume NJsonSchema via NuGet only. -- **Bump NJsonSchema package references** to the released stable `12.0.0`. +- **`Directory.Build.props`**: flip `UseLocalNJsonSchemaProjects` default back to `false`, or remove the property entirely and drop the conditional `ItemGroup` blocks from each csproj. NSwag `master` historically uses plain `PackageReference` only; decide before the final merge whether to keep the conditional pattern (allows future major-version dev to reuse it) or revert to the simpler form. +- **Bump NJsonSchema package references** in `Directory.Packages.props` to the released stable `12.0.0` (or later). - **`docs/plan_v15.md` and `docs/changelog_v15.md`**: move or archive after release — either delete once v15 is GA, or relocate to `docs/releases/` as historical context. - **Any `[Obsolete]` shims** added specifically to ease the v14→v15 migration: remove or mark for removal in v16. diff --git a/src/NSwag.AspNet.WebApi/NSwag.AspNet.WebApi.csproj b/src/NSwag.AspNet.WebApi/NSwag.AspNet.WebApi.csproj index a19373901..d115c9a41 100644 --- a/src/NSwag.AspNet.WebApi/NSwag.AspNet.WebApi.csproj +++ b/src/NSwag.AspNet.WebApi/NSwag.AspNet.WebApi.csproj @@ -7,6 +7,12 @@ + + + + + + diff --git a/src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj b/src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj index 9959284aa..48fc660c0 100644 --- a/src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj +++ b/src/NSwag.CodeGeneration.CSharp/NSwag.CodeGeneration.CSharp.csproj @@ -12,7 +12,10 @@ - + + + + diff --git a/src/NSwag.CodeGeneration.Tests/NSwag.CodeGeneration.Tests.csproj b/src/NSwag.CodeGeneration.Tests/NSwag.CodeGeneration.Tests.csproj index e827c0105..bc874f939 100644 --- a/src/NSwag.CodeGeneration.Tests/NSwag.CodeGeneration.Tests.csproj +++ b/src/NSwag.CodeGeneration.Tests/NSwag.CodeGeneration.Tests.csproj @@ -8,7 +8,6 @@ - @@ -17,6 +16,13 @@ + + + + + + + diff --git a/src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj b/src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj index 0caf9128d..781babd9e 100644 --- a/src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj +++ b/src/NSwag.CodeGeneration.TypeScript/NSwag.CodeGeneration.TypeScript.csproj @@ -12,7 +12,10 @@ - + + + + \ No newline at end of file diff --git a/src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj b/src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj index 4bb62721b..7ad9d27e5 100644 --- a/src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj +++ b/src/NSwag.CodeGeneration/NSwag.CodeGeneration.csproj @@ -4,7 +4,10 @@ true - + + + + diff --git a/src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj b/src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj index 94c45c36a..c91533994 100644 --- a/src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj +++ b/src/NSwag.Core.Yaml/NSwag.Core.Yaml.csproj @@ -4,7 +4,10 @@ NSwag - + + + + diff --git a/src/NSwag.Core/NSwag.Core.csproj b/src/NSwag.Core/NSwag.Core.csproj index 685b4cea4..63affeb27 100644 --- a/src/NSwag.Core/NSwag.Core.csproj +++ b/src/NSwag.Core/NSwag.Core.csproj @@ -6,7 +6,10 @@ true - + + + + diff --git a/src/NSwag.Generation/NSwag.Generation.csproj b/src/NSwag.Generation/NSwag.Generation.csproj index fc12ea958..1c83f7960 100644 --- a/src/NSwag.Generation/NSwag.Generation.csproj +++ b/src/NSwag.Generation/NSwag.Generation.csproj @@ -6,7 +6,10 @@ $(NoWarn),618 - + + + + From 9bc06ed9b5245aed6af013f4db1077ad0705e4bd Mon Sep 17 00:00:00 2001 From: Rico Suter Date: Wed, 22 Apr 2026 00:38:28 +0200 Subject: [PATCH 3/5] Add CLAUDE.md guide for contributor and Claude sessions - Project Overview: what NSwag is, its distribution channels, NJsonSchema dep - Cross-check with NJsonSchema: direction of change, when NSwag work needs NJsonSchema awareness (extension/overrides, API limitations, spec keyword parity, dependency bumping during v15) - Supported spec targets: Swagger 2.0 / OpenAPI 3.0.x / OpenAPI 3.1, with a note that master is 3.0-oriented and v15 completes 3.1 support - Build and test, high-level architecture by project layer, v15 branch conventions, repo-wide settings (TreatWarningsAsErrors, strong-naming, artifacts output, ImplicitUsings/LangVersion) - Code style conventions (analyzer policy, no abbreviations, CRLF, AAA tests) - Git rule: no AI attribution in commits / PRs / comments --- CLAUDE.md | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..222a52cf1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,97 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +NSwag is an OpenAPI/Swagger toolchain for .NET and TypeScript: it generates OpenAPI documents from ASP.NET / Web API controllers, generates C# and TypeScript client/controller code from OpenAPI documents, serves Swagger UI / ReDoc at runtime, and ships a Windows GUI (NSwagStudio), an MSBuild target, a CLI (including an npm wrapper), and a Chocolatey-distributed installer. It's built on top of [NJsonSchema](https://github.com/RicoSuter/NJsonSchema) (sibling repo), which provides the JSON Schema object model and code generation primitives. + +## Cross-check with NJsonSchema + +NSwag consumes NJsonSchema for the JSON Schema object model, `$ref` resolution, reflection-based schema generation, and C#/TypeScript type generation. A sibling clone of NJsonSchema is expected at `../NJsonSchema`. + +**Direction of change.** Most NSwag work stays within NSwag. When an NSwag task reveals an NJsonSchema limitation or needs a new capability, don't paper over it — open an NJsonSchema PR first. The reverse direction (NJsonSchema changes affecting NSwag) is governed by NJsonSchema's CLAUDE.md → "Cross-check with NSwag" section. + +**When an NSwag change does need NJsonSchema awareness:** + +1. **Extending NJsonSchema types.** NSwag subclasses `JsonSchema` (e.g. `OpenApiParameter : JsonSchema` overrides `ActualSchema`), relies on virtual members via overrides, and uses `InternalsVisibleTo` grants in a few places. Read `../NJsonSchema/docs/references.md` for reference-resolution mechanics before adding new overrides or relying on specific `ActualSchema` / `ActualTypeSchema` behavior. +2. **Hitting an NJsonSchema API limitation.** Open an issue or PR on NJsonSchema. Don't patch NSwag to work around missing or broken NJsonSchema behavior — the workaround becomes load-bearing and blocks the eventual fix. +3. **Spec keyword parity.** If NSwag starts emitting or reading an OpenAPI keyword that has a JSON Schema counterpart, verify NJsonSchema supports the same keyword across the same draft versions. Mismatches here are usually platform-level issues worth fixing on NJsonSchema. +4. **Bumping the NJsonSchema dependency.** While v15 builds against sibling project refs (`UseLocalNJsonSchemaProjects=true`), no version bump is needed day-to-day; just commit on both sides. When v15 switches to NuGet for release, update the version in `Directory.Packages.props`. + +### Supported spec targets + +NSwag supports, both reading and writing: + +- **Swagger 2.0** (OpenAPI 2.0) — JSON Schema Draft 4 subset. +- **OpenAPI 3.0.x** — Draft 5 subset; nullability expressed via `nullable: true`. +- **OpenAPI 3.1** — aligned with JSON Schema 2020-12 (native `const`, arbitrary `$ref` siblings, type-array nullability). Full 3.1 support depends on NJsonSchema v12 and lands on the `v15` branch. + +`master` / v14.x is primarily 3.0-oriented; `v15` completes 3.1 support. Code that reads or emits an OpenAPI document should behave sensibly across all three formats — the `OpenApiDocument` abstraction paves over most differences, but spec-specific keywords (e.g. `nullable`, `const`) still need branches on `SchemaType`. + +## Build and test + +The build is driven by [NUKE](https://nuke.build/) via `build.cmd` / `build.sh` / `build.ps1` (all three bootstrap the same `build/_build.csproj`). The SDK version in `global.json` (currently `10.0.100`, `rollForward: latestMinor`) is required — the bootstrapper will download a local copy if the system SDK is missing. + +Common targets (run from repo root): + +``` +./build.cmd Compile # Restore + build the solution +./build.cmd Test # Compile + run all *.Tests projects +./build.cmd Pack # Build NuGet packages, MSI, NSwag.zip / NSwag.Npm.zip (Windows only) +./build.cmd Publish # Push packages (CI only — needs *_API_KEY env vars) +``` + +Working directly with `dotnet` is fine for day-to-day iteration: + +``` +dotnet build src/NSwag.sln # Windows full solution (includes WiX installer) +dotnet build src/NSwag.NoInstaller.slnf # Linux/macOS filtered solution (no WiX) +dotnet test src/NSwag.CodeGeneration.CSharp.Tests/NSwag.CodeGeneration.CSharp.Tests.csproj +dotnet test src/NSwag.CodeGeneration.CSharp.Tests/... --filter "FullyQualifiedName~ClientGenerationTests" +``` + +The NUKE `Compile` target also publishes `NSwag.Console{,.x86}` (net462) and `NSwag.ConsoleCore` (net8.0/9.0/10.0) and copies the binaries into `src/NSwag.Npm/bin/binaries` and (on Windows) the NSwagStudio output. Skipping NUKE and running `dotnet build` alone will not lay out those binaries — use the NUKE target when testing the CLI/NPM/Studio distributions. + +## High-level architecture + +NSwag is a layered toolchain for OpenAPI/Swagger → .NET & TypeScript. Projects under `src/` are organized by layer: + +- **Specification** — `NSwag.Core`, `NSwag.Core.Yaml`, `NSwag.Annotations`: the `OpenApiDocument` object model, (de)serialization, attributes used to decorate controllers. +- **Generation (spec from code)** — `NSwag.Generation`, `NSwag.Generation.WebApi`, `NSwag.Generation.AspNetCore`: walk ASP.NET (Core) / Web API controllers to produce an `OpenApiDocument`. +- **CodeGeneration (code from spec)** — `NSwag.CodeGeneration` (shared base), `NSwag.CodeGeneration.CSharp`, `NSwag.CodeGeneration.TypeScript`: read `OpenApiDocument`, emit C# clients/controllers or TypeScript clients. Templates are `*.liquid` files embedded as resources under each project's `Templates/` folder. +- **Hosting middleware** — `NSwag.AspNetCore`, `NSwag.AspNet.Owin`, `NSwag.AspNet.WebApi`: serve the generated spec + Swagger UI / ReDoc at runtime. +- **Frontends** — `NSwag.Commands` (command object model), `NSwag.ConsoleCore` / `NSwag.Console{,.x86}` (CLIs), `NSwag.MSBuild` (MSBuild target), `NSwag.ApiDescription.Client` (`ServiceProjectReference` SDK), `NSwag.AssemblyLoader` (isolated-AppDomain loader for the CLI), `NSwag.Npm` (npm package wrapping the CLI), `NSwagStudio*` (WPF GUI + Chocolatey + WiX installer). + +NSwag depends heavily on **NJsonSchema** (sibling repo [`RicoSuter/NJsonSchema`](https://github.com/RicoSuter/NJsonSchema)) for the JSON Schema object model and for C#/TypeScript type generation. Most code gen works by composing an NJsonSchema `*TypeResolver` + `*Generator` with NSwag's client/controller templates on top. + +Multi-targeting: specification/generation projects typically target `netstandard2.0;net462;net8.0` (plus net9/net10 where relevant); hosting packages target `net462` or `netstandard` plus the modern `net8.0`/`net9.0`/`net10.0` ASP.NET Core targets — see `Directory.Packages.props` for the per-TFM package version table (central package management is enabled). + +Tests use **xUnit v3** + **Verify.XunitV3** for snapshot assertions — expected output lives under `Snapshots/*.verified.txt` next to each test project. When generator output intentionally changes, a `.received.txt` file will appear; review, rename to `.verified.txt`, and commit. `NSwag.CodeGeneration.CSharp.Tests` also shells out to a C# compiler (`CSharpCompiler.cs`) to confirm generated code actually builds. + +## v15 branch conventions (current branch: `docs/v15-setup` / PRs target `v15`) + +- **NJsonSchema is consumed via local project references**, not NuGet, while v15 is in development. This is controlled by `true` in `Directory.Build.props`. Every `*.csproj` that uses NJsonSchema has paired `ItemGroup Condition=...` blocks — leave both arms in sync when adding a new NJsonSchema reference. +- **Expected local layout**: `../NJsonSchema` (on the `v12` branch) as a sibling to this checkout. CI clones it explicitly; locally you must check it out yourself. +- **Feature PRs target `v15`**, not `master`. `master` is the v14.x stable line. See `docs/plan_v15.md` for the full branch model, release plan, and pre-release cleanup checklist. +- **User-visible changes must update `docs/changelog_v15.md`** (add under `Unreleased` → Breaking / New / Fixes, plus a Migration guide section if it breaks v14 consumers). +- **CI workflow YAMLs are auto-generated by NUKE** (`build/Build.CI.GitHubActions.cs`). The sibling-NJsonSchema `git clone` step in `.github/workflows/{build,pr}.yml` is a **hand edit** on the v15 branch — regenerating via `nuke --generate-configuration ...` will delete it. Don't regenerate without re-adding the clone step (or wait until v15 flips to NuGet-based NJsonSchema). + +## Repo-wide settings worth knowing + +- **`TreatWarningsAsErrors=true`** for all projects (`Directory.Build.props`). NuGet audit warnings `NU1901-NU1904` are exempt. New warnings from analyzers (`AnalysisLevel=latest-Recommended`, `EnforceCodeStyleInBuild=true`) will break the build — fix them rather than suppressing locally. +- **Assemblies are strong-named** with `NSwag.snk`. `InternalsVisibleTo` entries in `.csproj` files include the full public key — copy an existing entry when adding a new internals-visible consumer. +- **`true`** — all build outputs land under the repo-root `artifacts/` directory (not per-project `bin/`), which is what the NUKE build and the packaging logic assume. +- **`enable`** and `latest` everywhere. + +## Code style + +- C# latest language version (`LangVersion=latest`) with implicit usings enabled. +- **Warnings as errors** with analyzer level `latest-Recommended` and `EnforceCodeStyleInBuild=true`. New analyzer warnings will break the build — fix them rather than adding local suppressions (the `NoWarn` lists in `Directory.Build.props` are the established exceptions). +- **No abbreviations in variable / field / parameter names** (e.g. `attribute` not `attr`, `property` not `prop`, `parameter` not `param`). +- **4-space indentation, CRLF line endings** (except `.verified.txt` snapshot files, which are LF). +- Tests follow the **AAA pattern** (`// Arrange`, `// Act`, `// Assert` comments) matching existing test style. + +## Git Rules + +- Never include "Claude", "Co-Authored-By", or AI attribution in commit messages, PR descriptions, or GitHub comments. From 3987922ad9ede3909d054afbd81d777d19697b75 Mon Sep 17 00:00:00 2001 From: Rico Suter Date: Wed, 22 Apr 2026 00:54:57 +0200 Subject: [PATCH 4/5] Pre-build NJsonSchema in Release before NSwag CI build CI was failing with MSB3030 during NSwag.ConsoleCore publish: "Could not copy the file .../NJsonSchema/artifacts/bin/.../release_net8.0/ NJsonSchema.dll because it was not found." Root cause: with UseArtifactsOutput=true on both repos, MSBuild's Configuration=Release property does not reliably propagate across the repo boundary when NSwag builds NJsonSchema project references transitively. NJsonSchema ends up compiled to artifacts/bin/*/debug_* while NSwag expects release_* for the --no-build publish step, so publish fails. Fix: add an explicit pre-build step after the sibling NJsonSchema clone that runs 'dotnet build NJsonSchema.sln -c Release' in ../NJsonSchema. The Release artifacts are then present when NSwag's build invokes dotnet publish --no-build. Doc updated in plan_v15.md with the rationale. --- .github/workflows/build.yml | 9 +++++++++ .github/workflows/pr.yml | 9 +++++++++ docs/plan_v15.md | 5 +++++ 3 files changed, 23 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 95b24f748..7b79990f5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,6 +48,9 @@ jobs: - uses: actions/checkout@v6 - name: 'Clone NJsonSchema (v12) as sibling for project references' run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema + - name: 'Pre-build NJsonSchema (v12) in Release so project-ref artifacts exist' + run: dotnet build src/NJsonSchema.sln -c Release -v minimal + working-directory: ../NJsonSchema - name: 'Run: Compile, Test, Pack, Publish' run: ./build.cmd Compile Test Pack Publish env: @@ -86,6 +89,9 @@ jobs: - uses: actions/checkout@v6 - name: 'Clone NJsonSchema (v12) as sibling for project references' run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema + - name: 'Pre-build NJsonSchema (v12) in Release so project-ref artifacts exist' + run: dotnet build src/NJsonSchema.sln -c Release -v minimal + working-directory: ../NJsonSchema - name: 'Run: Compile, Test, Pack, Publish' run: ./build.cmd Compile Test Pack Publish env: @@ -104,6 +110,9 @@ jobs: - uses: actions/checkout@v6 - name: 'Clone NJsonSchema (v12) as sibling for project references' run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema + - name: 'Pre-build NJsonSchema (v12) in Release so project-ref artifacts exist' + run: dotnet build src/NJsonSchema.sln -c Release -v minimal + working-directory: ../NJsonSchema - name: 'Run: Compile, Test, Pack, Publish' run: ./build.cmd Compile Test Pack Publish env: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 8a31e358a..a8c9f0e43 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -50,6 +50,9 @@ jobs: - uses: actions/checkout@v6 - name: 'Clone NJsonSchema (v12) as sibling for project references' run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema + - name: 'Pre-build NJsonSchema (v12) in Release so project-ref artifacts exist' + run: dotnet build src/NJsonSchema.sln -c Release -v minimal + working-directory: ../NJsonSchema - name: 'Run: Compile, Test, Pack' run: ./build.cmd Compile Test Pack - name: 'Publish: NSwag.zip' @@ -83,6 +86,9 @@ jobs: - uses: actions/checkout@v6 - name: 'Clone NJsonSchema (v12) as sibling for project references' run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema + - name: 'Pre-build NJsonSchema (v12) in Release so project-ref artifacts exist' + run: dotnet build src/NJsonSchema.sln -c Release -v minimal + working-directory: ../NJsonSchema - name: 'Run: Compile, Test, Pack' run: ./build.cmd Compile Test Pack macos-latest: @@ -96,5 +102,8 @@ jobs: - uses: actions/checkout@v6 - name: 'Clone NJsonSchema (v12) as sibling for project references' run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema + - name: 'Pre-build NJsonSchema (v12) in Release so project-ref artifacts exist' + run: dotnet build src/NJsonSchema.sln -c Release -v minimal + working-directory: ../NJsonSchema - name: 'Run: Compile, Test, Pack' run: ./build.cmd Compile Test Pack diff --git a/docs/plan_v15.md b/docs/plan_v15.md index 30412d993..94a62bdef 100644 --- a/docs/plan_v15.md +++ b/docs/plan_v15.md @@ -39,10 +39,15 @@ While v15 uses `UseLocalNJsonSchemaProjects=true`, the `v15` branch's CI workflo - uses: actions/checkout@v6 - name: 'Clone NJsonSchema (v12) as sibling for project references' run: git clone --depth 1 --branch v12 https://github.com/RicoSuter/NJsonSchema.git ../NJsonSchema +- name: 'Pre-build NJsonSchema (v12) in Release so project-ref artifacts exist' + run: dotnet build src/NJsonSchema.sln -c Release -v minimal + working-directory: ../NJsonSchema ``` Layout on the runner: `$GITHUB_WORKSPACE/../NJsonSchema` (sibling to the NSwag checkout), which matches the relative project-reference paths (`../../../NJsonSchema/src/…`). +**Why the pre-build step:** even though NSwag's solution build would nominally build NJsonSchema transitively via project references, MSBuild's `Configuration=Release` property doesn't reliably propagate across repo boundaries when both sides use `UseArtifactsOutput=true`. Without the pre-build, NJsonSchema compiles to `artifacts/bin/*/debug_*` while NSwag expects `release_*` for the final `dotnet publish --no-build` step on `NSwag.ConsoleCore`, and the publish fails with `MSB3030: Could not copy the file .../release_net8.0/NJsonSchema.dll`. Pre-building NJsonSchema in Release produces the expected artifacts at the expected paths. + **Note:** The NSwag workflow YAMLs are normally auto-generated by NUKE (`build/Build.CI.GitHubActions.cs`). The sibling-clone step is a hand edit on the `v15` branch only; NUKE regeneration would remove it. This is intentional — see the cleanup checklist below. ## Branch model From e11f184c192e3e7370dd8241d2471610b58ec28d Mon Sep 17 00:00:00 2001 From: Rico Suter Date: Wed, 22 Apr 2026 01:04:33 +0200 Subject: [PATCH 5/5] Update CLAUDE.md with v15 project documentation and conventions --- CLAUDE.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 222a52cf1..c9f24f25b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -69,7 +69,19 @@ Multi-targeting: specification/generation projects typically target `netstandard Tests use **xUnit v3** + **Verify.XunitV3** for snapshot assertions — expected output lives under `Snapshots/*.verified.txt` next to each test project. When generator output intentionally changes, a `.received.txt` file will appear; review, rename to `.verified.txt`, and commit. `NSwag.CodeGeneration.CSharp.Tests` also shells out to a C# compiler (`CSharpCompiler.cs`) to confirm generated code actually builds. -## v15 branch conventions (current branch: `docs/v15-setup` / PRs target `v15`) +## Project docs + +| File | Read when | +|------|-----------| +| `docs/plan_v15.md` | Working on the `v15` branch — scope, branch model, pre-release cleanup checklist | +| `docs/changelog_v15.md` | Landing any user-visible change on `v15` — **must be updated** (Breaking / New / Fixes + Migration guide entry if the change breaks v14 consumers) | +| `../NJsonSchema/docs/references.md` | Touching `$ref` resolution, `ActualSchema` / `ActualTypeSchema`, overrides like `OpenApiParameter.ActualSchema`, or spec-keyword parity issues | +| `../NJsonSchema/docs/plan_v12.md` | Coordinating NSwag v15 work with upstream NJsonSchema v12 changes | +| `../NJsonSchema/docs/changelog_v12.md` | Understanding which NJsonSchema breaking changes cascade into v15 — link from `changelog_v15.md` entries that absorb upstream changes | + +Keep this index up to date — if a new persistent doc lands under `docs/`, add a row with a concrete "read when" trigger. + +## v15 branch conventions - **NJsonSchema is consumed via local project references**, not NuGet, while v15 is in development. This is controlled by `true` in `Directory.Build.props`. Every `*.csproj` that uses NJsonSchema has paired `ItemGroup Condition=...` blocks — leave both arms in sync when adding a new NJsonSchema reference. - **Expected local layout**: `../NJsonSchema` (on the `v12` branch) as a sibling to this checkout. CI clones it explicitly; locally you must check it out yourself.