Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ee01e09
docs: add AI agent docs
jrgcubano Sep 30, 2026
1b02ba5
docs(plan): add net10 migration plan
jrgcubano Sep 30, 2026
c8b46b1
docs(plan): apply review decisions
jrgcubano Sep 30, 2026
092f622
feat(net10): target net10.0 and net8.0
jrgcubano Sep 30, 2026
908b3a0
chore(deps): move tests to xunit v3
jrgcubano Sep 30, 2026
42703a7
chore(build): upgrade cake to 6.0.0 with traversal build
jrgcubano Sep 30, 2026
ae558c6
ci: build and publish with .NET 10 SDK
jrgcubano Sep 30, 2026
e3bac20
build: validate packages against 1.4.0
jrgcubano Sep 30, 2026
5a0cd8a
build: start versions at 2.0
jrgcubano Sep 30, 2026
4fb7103
docs: update readme and contributing for net10
jrgcubano Sep 30, 2026
3d1d8e0
docs(plan): record local verification
jrgcubano Sep 30, 2026
4a4b936
docs: note package validation baseline
jrgcubano Sep 30, 2026
9d2b815
docs(plan): record ci verification
jrgcubano Sep 30, 2026
f96bacd
chore(deps): bump Newtonsoft.Json to 13.0.4
jrgcubano Sep 30, 2026
b438b45
chore(build): drop traversal build, keep config.yml
jrgcubano Sep 30, 2026
1bdc58a
chore(build): migrate the solution to slnx
jrgcubano Sep 30, 2026
b9b906a
chore(build): update cake to 6.3.0
jrgcubano Oct 1, 2026
88559f4
ci: hash the tool and package manifests in the cache key
jrgcubano Oct 1, 2026
bb11a52
test: run the tests on net8.0 too
jrgcubano Oct 1, 2026
ec54c06
build: restore from nuget.org only
jrgcubano Oct 1, 2026
9e7f4d2
chore(deps): drop the Source Link package
jrgcubano Oct 1, 2026
7655451
docs(plan): apply review comments
jrgcubano Oct 1, 2026
92f070a
docs(plan): align tasks with the net8.0 test run
jrgcubano Oct 1, 2026
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
5 changes: 1 addition & 4 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,10 @@ indent_size = 4
insert_final_newline = false
trim_trailing_whitespace = true

[*.sln]
indent_style = tab

[*.{csproj,vbproj,vcxproj,vcxproj.filters}]
indent_size = 2

[*.{xml,config,props,targets,nuspec,ruleset}]
[*.{xml,config,props,targets,nuspec,ruleset,slnx}]
indent_size = 2

[*.{yml,yaml}]
Expand Down
28 changes: 15 additions & 13 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Cache packages
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ env.NUGET_PACKAGES }}
key: ${{ runner.os }}-nuget-${{ hashFiles('.config/dotnet-tools.json', '**/packages.lock.json') }}
key: ${{ runner.os }}-nuget-${{ hashFiles('dotnet-tools.json', 'src/Directory.Packages.props') }}
restore-keys: ${{ runner.os }}-nuget-

- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
- name: Setup .NET SDK (global.json + .NET 8 for tests)
uses: actions/setup-dotnet@v5
with:
global-json-file: global.json
dotnet-version: 8.0.x

- name: Integration Tests
shell: bash
Expand All @@ -53,23 +56,22 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Cache packages
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ env.NUGET_PACKAGES }}
key: ${{ runner.os }}-nuget-${{ hashFiles('.config/dotnet-tools.json', '**/packages.lock.json') }}
key: ${{ runner.os }}-nuget-${{ hashFiles('dotnet-tools.json', 'src/Directory.Packages.props') }}
restore-keys: ${{ runner.os }}-nuget-

- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
- name: Setup .NET SDK (global.json + .NET 8 for tests)
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
6.0.x
8.0.x
global-json-file: global.json
dotnet-version: 8.0.x

- name: Integration Tests
shell: bash
Expand Down
15 changes: 7 additions & 8 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,22 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Cache packages
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ env.NUGET_PACKAGES }}
key: ${{ runner.os }}-nuget-${{ hashFiles('.config/dotnet-tools.json', '**/packages.lock.json') }}
key: ${{ runner.os }}-nuget-${{ hashFiles('dotnet-tools.json', 'src/Directory.Packages.props') }}
restore-keys: ${{ runner.os }}-nuget-

- name: Setup .NET SDK
uses: actions/setup-dotnet@v3
- name: Setup .NET SDK (global.json + .NET 8 for tests)
uses: actions/setup-dotnet@v5
with:
dotnet-version: |
6.0.x
8.0.x
global-json-file: global.json
dotnet-version: 8.0.x

- name: Publish
shell: bash
Expand Down
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ dotnet-install.sh*
.vs/
.vscode/
launchSettings.json
*.sln.ide/

# Rider
src/.idea/**/workspace.xml
Expand All @@ -41,7 +40,6 @@ src/.idea/**/uiDesigner.xml
# User-specific files
*.suo
*.user
*.sln.docstates
*.userprefs
*.GhostDoc.xml
*StyleCop.Cache
Expand Down
59 changes: 59 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# AGENTS.md

Guidance for AI coding agents and contributors working in this repository.

## Overview

GeoJSON model types plus JSON converters for Newtonsoft.Json and System.Text.Json, published to
nuget.org as three packages:

| Package | Contents |
|---------|----------|
| `Appy.Spatial.GeoJSON` | Model: `Feature`, `FeatureCollection`, geometries, `Crs`, `BoundingBox` |
| `Appy.Spatial.GeoJSON.Newtonsoft` | `FeatureConverter`, `GeometryConverter`, `JsonSerializerSettings.UseGeoJsonConverters()` |
| `Appy.Spatial.GeoJSON.TextJson` | `FeatureConverter`, `GeometryConverter`, `JsonSerializerOptions.UseGeoJsonConverters()` |

Architecture and CI: [docs/Architecture.md](docs/Architecture.md).

## Build Commands

```bash
dotnet tool restore # Cake, MinVer CLI, gpr
dotnet cake # Default target: Clean, Build, Test, Package (.artifacts/)
dotnet test src/Appy.Spatial.GeoJSON.slnx
dotnet test src/Appy.Spatial.GeoJSON.slnx --filter "FullyQualifiedName~SerialisationTests.ShouldRoundTripPoint"
```

- `build.cake` builds the projects listed in `config.yml` (`Type: Package` or `Type: Test`); add new
projects there.
- Cake builds treat all warnings as errors.
- `Publish` and `Publish-Package-*` targets push to nuget.org and GitHub Packages. They run only in
GitHub Actions; never run them locally.

## Conventions

- **Central package management**: versions live in `src/Directory.Packages.props`; `PackageReference`
items carry no `Version`. Framework-specific versions use a `Condition` on `$(TargetFramework)`.
- **Shared metadata**: package info, Source Link settings (built into the SDK) and MinVer are in
`src/Directory.Build.props`.
- **Versioning**: MinVer derives the version from git tags (`1.4.0`). No version is stored in files.
- **Package validation**: `dotnet pack` checks each package against the last release
(`PackageValidationBaselineVersion` in `src/Directory.Build.targets`). API breaks fail the build.
- **Tests**: xUnit v3 + FluentAssertions, run on `net10.0` and `net8.0`. Every geometry round-trips
through both serializers, as the base type and the concrete type, bare and inside a `Feature`.
Keep both converter packages in step.
- **Package source**: `nuget.config` restores from nuget.org only.
- **Target frameworks** are set per `.csproj`; the SDK is pinned in `global.json`.
- **Style**: `.editorconfig` (4 spaces, 2 for XML/JSON/YAML, CRLF). File-scoped namespaces.
- **Public repo**: no secrets, internal hostnames or private feed URLs in code, docs or commits.

## Git Conventions

- Branch: `feat/<desc>`, `fix/<desc>`, `docs/<desc>`.
- Commits: conventional commits, one line, e.g. `feat(net8): add net8 and drop support for net7`.
See [CONTRIBUTING.md](CONTRIBUTING.md).

## Documentation

- [docs/Architecture.md](docs/Architecture.md): overview, CI and release flow, plans
- [docs/plans/](docs/plans/): implementation plans
13 changes: 13 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# CLAUDE.md

@./AGENTS.md

## Build & Test

```bash
dotnet tool restore
dotnet cake # Default: clean, build, test, pack into .artifacts/
dotnet test src/Appy.Spatial.GeoJSON.slnx # tests only
```

Never run the `Publish` cake target or `dotnet nuget push`: publishing happens only in GitHub Actions.
3 changes: 2 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ Before you submit your pull request consider the following guidelines:
* Build your changes locally to ensure all the tests pass:

```shell
build.ps1
dotnet tool restore
dotnet cake
```

* Push your branch to GitHub:
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ AppyWay geojson model and json serialization converters.
| [Appy.Spatial.GeoJSON.Newtonsoft](https://www.nuget.org/packages/Appy.Spatial.GeoJSON.Newtonsoft) | [![Nuget Package](https://img.shields.io/badge/nuget-1.4.0-blue.svg)](https://www.nuget.org/packages/Appy.Spatial.GeoJSON.Newtonsoft) |
| [Appy.Spatial.GeoJSON.TextJson](https://www.nuget.org/packages/Appy.Spatial.GeoJSON.TextJson) | [![Nuget Package](https://img.shields.io/badge/nuget-1.4.0-blue.svg)](https://www.nuget.org/packages/Appy.Spatial.GeoJSON.TextJson) |

## Supported frameworks

The packages target `net10.0` and `net8.0`; apps on `net9.0` use the `net8.0` build.
Version 2.0 dropped `netstandard2.0` and `net6.0`: .NET Framework and older .NET apps should stay
on 1.4.x.

## Contribute
It would be awesome if you would like to contribute code or help with bugs. Just follow the guidelines [CONTRIBUTING](https://github.com/YellowLineParking/Appy.Spatial.GeoJSON/blob/master/CONTRIBUTING.md)
5 changes: 3 additions & 2 deletions build.cake
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#addin nuget:?package=YamlDotNet&version=13.7.1
#addin nuget:?package=YamlDotNet&version=16.2.0
#addin nuget:?package=System.Xml.XDocument&version=4.3.0
#addin nuget:?package=Cake.MinVer&version=3.0.0
#addin nuget:?package=Cake.MinVer&version=4.0.0
#addin nuget:?package=Cake.Yaml&version=6.0.0

#load "./functions.cake"
Expand All @@ -14,6 +14,7 @@ var configFilePath = "config.yml";
var taskConfigManager = new ProjectTaskConfigurationManager();
var projectDescriptors = ProjectLoader.Load(Context, configFilePath, basePath, configuration).Projects;
var version = MinVer(settings => settings
.WithMinimumMajorMinor("2.0")
.WithDefaultPreReleasePhase("preview")
.WithVerbosity(MinVerVerbosity.Info));

Expand Down
53 changes: 53 additions & 0 deletions docs/Architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Appy.Spatial.GeoJSON Architecture

## Overview

A small library: plain GeoJSON model classes in `Appy.Spatial.GeoJSON`, and two converter packages
that teach Newtonsoft.Json and System.Text.Json how to read polymorphic geometries and features.
The model has no serializer dependency, so consumers pick one converter package.

Build, test and conventions: [AGENTS.md](../AGENTS.md).

## System Diagram

```mermaid
graph TD
Core["Appy.Spatial.GeoJSON<br/>model"]
NS["Appy.Spatial.GeoJSON.Newtonsoft"] --> Core
STJ["Appy.Spatial.GeoJSON.TextJson"] --> Core
NS --> NJ["Newtonsoft.Json"]
Tests["Appy.Spatial.GeoJSON.Tests"] --> NS
Tests --> STJ
```

## Key Patterns

### Type discriminator

Geometries derive from `Geometry` / `Geometry<TCoordinates>` and features from `Feature`,
`Feature<TGeometry>` and `Feature<TGeometry, TProperties>`. The converters handle only the abstract
`Geometry` and `Feature` targets: they read the GeoJSON `type` property first (the geometry's
`type` for features), then deserialize into the concrete class (`Point`, `LineString`, `Polygon`, `MultiLineString`,
`MultiPolygon`, `GeometryCollection`). Unknown types throw the serializer's JSON exception.

### Opt-in registration

Each converter package exposes one extension, `UseGeoJsonConverters()`, on `JsonSerializerSettings`
(Newtonsoft) or `JsonSerializerOptions` (System.Text.Json). It adds `FeatureConverter` and
`GeometryConverter`.

## CI and Release

| Workflow | Trigger | Does |
|----------|---------|------|
| `.github/workflows/ci.yaml` | pull request | `dotnet cake` on Windows and macOS, then Linux |
| `.github/workflows/publish.yaml` | push to `master` touching `src/**`, or any tag | `dotnet cake --target=Publish`: packs, pushes to nuget.org and GitHub Packages |

MinVer computes the version from the latest tag: a tag publishes that release, and a `master` push
publishes a `-preview` build with the commit height.

## Plans

| Plan | Status | Summary |
|------|--------|---------|
| [001 .NET 10 Migration](plans/001-net10-migration.md) | In Progress | `net10.0;net8.0`, SDK 10, Cake 6, xUnit v3; release 2.0.0 |
95 changes: 95 additions & 0 deletions docs/plans/001-net10-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# .NET 10 Migration

- **Status:** In Progress
- **Branch:** `feat/net10`

## Summary

Move the three packages to `net10.0;net8.0` on the .NET 10 SDK, drop `net6.0` and `netstandard2.0`,
and bring the Cake build, GitHub Actions and test stack in line with
[Appy.Configuration](https://github.com/YellowLineParking/Appy.Configuration). Released as
**2.0.0**. No public API change.

## Decisions

| Decision | Context | Alternatives Considered |
|----------|---------|------------------------|
| Libraries `net10.0;net8.0`, tests `net10.0;net8.0` | The two LTS releases. `net9.0` apps resolve the `net8.0` asset; `net6.0` is out of support. Tests run on both because `net8.0` uses the in-box System.Text.Json 8 | Also `net9.0`: no API gain. Keep `netstandard2.0`: keeps the nullable shim and the System.Text.Json package for no current need |
| Version **2.0.0** | Dropping `netstandard2.0` ends .NET Framework and pre-net8 support: breaking under SemVer. Those users stay on 1.4.x | Minor bump (Appy.Configuration used 1.2.0 for the same drop): hides the break |
| Remove the `System.Text.Json` package | In-box on net8+; the package only served `netstandard2.0`/`net6.0` | Keep it: NU1510 pruning warning, fails the warnings-as-errors build |
| `Newtonsoft.Json` **13.0.4** | Latest stable. Apps that pin a lower version directly must bump it with 2.0.0 (NU1605) | Keep 13.0.3: no advisory against it, but stays behind latest |
| Tests: `xunit.v3` 3.2.2, `xunit.runner.visualstudio` 3.1.5, FluentAssertions 7.2.2, `Microsoft.NET.Test.Sdk` 18.10.1, `GitHubActionsTestLogger` 2.4.1 | xUnit v2 is in maintenance. FluentAssertions 8 needs a commercial licence. Logger 3.x and `XunitXml.TestLogger` 8.x need Microsoft.Testing.Platform 2 (CS1705 against xunit.v3 3.2.2's 1.9.1); the XML logger was unused | xUnit 2.9.3 (as in Appy.Configuration); `xunit.v3` 4.x |
| Build tooling as Appy.Configuration: Cake 6.3.0, Cake.MinVer 4.0.0, Cake.Yaml 6.0.0, YamlDotNet 16.2.0, MinVer 2.3.0; Source Link from the SDK (no `Microsoft.SourceLink.GitHub` package); `build.cake` builds the `config.yml` projects one by one | Same pipeline across repos; MinVer 2.3.0 keeps versioning unchanged | Traversal `src/build.csproj`: not adopted yet. MinVer 8: new pre-release options, out of scope |
| Solution as `src/Appy.Spatial.GeoJSON.slnx` | SDK 10 format; the name now matches the package casing | Keep the classic solution format |
| CI installs the SDK in `global.json` plus 8.0.x | SDK 10 builds both targets; 8.0.x provides the .NET 8 runtime for the `net8.0` test run; the Cake, MinVer and gpr tools roll forward | SDK 10 only: leaves the `net8.0` assets untested |
| `nuget.config` with nuget.org only | CPM raises NU1507 when a machine has several package sources; one repo-level source keeps restores reproducible | Suppress NU1507 (as Appy.Configuration does): hides where packages come from |
| Package validation against 1.4.0 | Catches accidental API breaks; only the dropped TFMs are suppressed (PKV006) | None; Appy.Configuration has no validation |
| `MinVerMinimumMajorMinor` 2.0 (MSBuild and `build.cake`) | The master push that follows the merge publishes a preview. Without this it is `1.4.1-preview`, carrying breaking changes. Cake computes the package path, so both must agree | Skip it and accept one misleading preview |

## Tasks

- [x] 1. `feat(net10): target net10.0 and net8.0`. `global.json` SDK `10.0.103`,
`rollForward: latestFeature`. The three library csproj files go to `net10.0;net8.0`, tests
to `net10.0` (later `net10.0;net8.0`, from review). Remove the `netstandard2.0` shims (`Nullable` package, `PackageDownload`
`Microsoft.NETCore.App.Ref`, `AnnotatedReferenceAssemblyVersion`, per-project `LangVersion`) and
the `System.Text.Json` package. `PackageTags` become `NET10;NET8`.
Test: the 6 `SerialisationTests` pass on `net10.0` and `net8.0`
(`dotnet test src/Appy.Spatial.GeoJSON.slnx`).
- [x] 2. `chore(deps): move tests to xunit v3`. Update `src/Directory.Packages.props` to the test
stack above. Remove unused entries (`Moq`, `MartinCostello.Logging.XUnit`,
`TunnelVisionLabs.ReferenceAssemblyAnnotator`, `coverlet.collector`, `XunitXml.TestLogger`) and the
`Version="$(...)"` attributes in the test csproj.
Test: same 6 tests pass. The run reports a non-zero count.
- [x] 3. `chore(build): upgrade cake to 6.0.0`, then 6.3.0. Update `dotnet-tools.json` and the `build.cake`
addins. `config.yml` still drives build, test, pack and publish (no traversal build).
Test: `dotnet tool restore && dotnet cake` is green. `.artifacts/` holds 3 nupkgs with
`lib/net8.0` and `lib/net10.0` only.
- [x] 4. `ci: build and publish with .NET 10 SDK`. In `ci.yaml` and `publish.yaml`, move to
`actions/checkout@v6`, `actions/cache@v5` and `actions/setup-dotnet@v5`, SDK from `global.json`
plus 8.0.x for the `net8.0` test run (from review). Triggers stay as they are.
Test: PR checks green on Windows, macOS and Linux.
- [x] 5. `build: validate packages against 1.4.0`. Add `EnablePackageValidation` and
`PackageValidationBaselineVersion` 1.4.0 to packable projects. Pack fails first on PKV006 (the
dropped TFMs). Then add a `CompatibilitySuppressions.xml` with PKV006 only.
Test: `dotnet cake` is green. No CP0xxx (API) diagnostics.
- [x] 6. `build: start versions at 2.0`. Set `MinVerMinimumMajorMinor` 2.0 in
`src/Directory.Build.targets` and `.WithMinimumMajorMinor("2.0")` in `build.cake`.
Test: `dotnet cake` logs `2.0.0-preview.0.N`. The nupkg names match the version Cake logs.
- [x] 7. `docs: update readme and contributing for net10`. The README gets a supported-frameworks
line and says that `netstandard2.0`/`net6.0` users should stay on 1.4.x. In `CONTRIBUTING.md`,
replace `build.ps1` with `dotnet tool restore && dotnet cake`.
Test: the links resolve.

## Verification

- [x] `dotnet cake` (Default target) green locally on SDK 10, warnings as errors, 6/6 tests on net10.0 and net8.0.
- [x] Nuspecs: TextJson has no `System.Text.Json` dependency; Newtonsoft depends on
`Newtonsoft.Json` >= 13.0.4; groups only for net8.0 and net10.0.
- [x] Package validation passes with only PKV006 suppressed.
- [x] PR CI green on all three OS jobs.
- [ ] Done when merged and nuget.org lists 2.0.0 for `Appy.Spatial.GeoJSON`,
`Appy.Spatial.GeoJSON.Newtonsoft` and `Appy.Spatial.GeoJSON.TextJson`.

## Release

Maintainers rebase-merge the PR; the master push publishes `2.0.0-preview.0.N`. Tagging `2.0.0` on
master publishes the release. Then `docs(readme): change packages version to 2.0.0` (badges), and
move `PackageValidationBaselineVersion` to 2.0.0 and delete the `CompatibilitySuppressions.xml` files.

## Key Files

```
global.json # SDK 10
dotnet-tools.json # Cake 6.3.0
nuget.config # nuget.org as the only package source (new)
build.cake # Cake pipeline, MinVer settings
config.yml # projects to build, test and pack
src/Directory.Build.props # package metadata
src/Directory.Build.targets # package validation, MinVer settings
src/*/CompatibilitySuppressions.xml # PKV006 for the dropped TFMs (new)
src/Directory.Packages.props # central package versions
src/Appy.Spatial.GeoJSON.slnx # solution (new, replaces the classic one)
src/*/*.csproj # target frameworks
.github/workflows/ci.yaml # PR build
.github/workflows/publish.yaml # publish on master push or tag
```
2 changes: 1 addition & 1 deletion dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"cake.tool": {
"version": "4.0.0",
"version": "6.3.0",
"commands": [
"dotnet-cake"
]
Expand Down
Loading
Loading