From 57c9aa6e00d393430a3f8c02e4c9c6a50905c7fe Mon Sep 17 00:00:00 2001 From: Mathias Beaulieu-Duncan Date: Mon, 6 Oct 2025 15:12:38 -0400 Subject: [PATCH] added nuget publish workflow --- .gitea/workflows/publish-nuget.yml | 69 +++++++++ CLAUDE.md | 60 +++++++- LICENSE | 21 +++ RELEASING.md | 134 ++++++++++++++++++ .../Models/Address.cs | 2 +- Svrnty.GeoManagement.Abstractions/README.md | 123 ++++++++++++++++ .../Svrnty.GeoManagement.Abstractions.csproj | 20 +++ .../Svrnty.GeoManagement.Google.csproj | 20 +++ 8 files changed, 445 insertions(+), 4 deletions(-) create mode 100644 .gitea/workflows/publish-nuget.yml create mode 100644 LICENSE create mode 100644 RELEASING.md create mode 100644 Svrnty.GeoManagement.Abstractions/README.md diff --git a/.gitea/workflows/publish-nuget.yml b/.gitea/workflows/publish-nuget.yml new file mode 100644 index 0000000..a2caa0a --- /dev/null +++ b/.gitea/workflows/publish-nuget.yml @@ -0,0 +1,69 @@ +name: Publish NuGet Packages + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+' + - '[0-9]+.[0-9]+.[0-9]+-*' + +jobs: + build-and-publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Extract version from tag + run: | + VERSION=${GITHUB_REF##*/} + echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "Publishing version: $VERSION" + + - name: Restore dependencies + run: dotnet restore + + - name: Build solution + run: dotnet build --configuration Release --no-restore + + - name: Run tests + run: dotnet test --configuration Release --no-build --verbosity normal + + - name: Pack Abstractions + run: | + dotnet pack Svrnty.GeoManagement.Abstractions/Svrnty.GeoManagement.Abstractions.csproj \ + --configuration Release \ + --no-build \ + --output ./artifacts \ + -p:Version=${{ env.VERSION }} + + - name: Pack Google Provider + run: | + dotnet pack Svrnty.GeoManagement.Google/Svrnty.GeoManagement.Google.csproj \ + --configuration Release \ + --no-build \ + --output ./artifacts \ + -p:Version=${{ env.VERSION }} + + - name: List packages + run: ls -lh ./artifacts + + - name: Publish to NuGet.org + run: | + dotnet nuget push ./artifacts/*.nupkg \ + --api-key ${{ secrets.NUGET_API_KEY }} \ + --source https://api.nuget.org/v3/index.json \ + --skip-duplicate + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: nuget-packages-${{ env.VERSION }} + path: ./artifacts/*.nupkg diff --git a/CLAUDE.md b/CLAUDE.md index df1b73b..12b3578 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,10 +8,11 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Solution Structure -The solution contains two projects: +The solution contains these projects: -- **Svrnty.GeoManagement.Abstractions**: Core abstractions and models for geocoding operations -- **Svrnty.GeoManagement.Tests**: xUnit test project +- **Svrnty.GeoManagement.Abstractions**: Core abstractions and models for geocoding operations (published to NuGet) +- **Svrnty.GeoManagement.Google**: Google Geocoding API provider implementation (published to NuGet) +- **Svrnty.GeoManagement.Tests**: xUnit integration test project ## Key Architecture Patterns @@ -56,3 +57,56 @@ dotnet restore - Nullable reference types are enabled - The Abstractions project is designed to be provider-agnostic - don't add provider-specific dependencies - When implementing `IGeoManagementProvider`, all methods support `CancellationToken` for async operations + +## NuGet Package Publishing + +### Published Packages + +- **Svrnty.GeoManagement.Abstractions** - Core interfaces and models +- **Svrnty.GeoManagement.Google** - Google Maps API provider + +### Release Process + +1. **Ensure all changes are committed and pushed** +2. **Create and push a git tag** with the version number: + ```bash + git tag 1.0.0 + git push origin 1.0.0 + ``` + +3. **Tag format**: + - Stable releases: `1.0.0`, `1.2.3` + - Pre-releases: `1.0.0-alpha`, `1.0.0-beta.1`, `1.0.0-rc.2` + +4. **Automated workflow** (`.gitea/workflows/publish-nuget.yml`): + - Triggers on any tag matching version pattern + - Runs all tests + - Builds Release configuration + - Packs both NuGet packages with the tag version + - Publishes to NuGet.org + +### Version Management + +- Package versions are automatically set from the git tag +- Default version in `.csproj` files is `0.1.0` (used only for local development) +- CI/CD overrides version with tag value during publish + +### Prerequisites + +- `NUGET_API_KEY` must be configured as a Gitea repository secret +- Tests must pass for publish to succeed +- Both packages are always published together with the same version + +### Local Package Testing + +To test package creation locally: +```bash +# Pack with custom version +dotnet pack Svrnty.GeoManagement.Abstractions -c Release -p:Version=1.0.0-test + +# Pack Google provider +dotnet pack Svrnty.GeoManagement.Google -c Release -p:Version=1.0.0-test + +# View generated packages +ls */bin/Release/*.nupkg +``` diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..15b11a0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Svrnty + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..a0cdd40 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,134 @@ +# Release Guide + +This guide explains how to publish new versions of the Svrnty.GeoManagement NuGet packages. + +## Prerequisites + +✅ All tests passing +✅ Changes committed and pushed to main branch +✅ `NUGET_API_KEY` configured in Gitea repository secrets + +## Release Steps + +### 1. Decide on Version Number + +Follow [Semantic Versioning](https://semver.org/): + +- **Major version** (x.0.0) - Breaking changes +- **Minor version** (0.x.0) - New features, backwards compatible +- **Patch version** (0.0.x) - Bug fixes, backwards compatible + +**Pre-release suffixes:** +- `-alpha` - Early testing +- `-beta` - Feature complete, testing +- `-rc.1` - Release candidate + +**Examples:** +- `1.0.0` - First stable release +- `1.1.0` - New features added +- `1.1.1` - Bug fix +- `2.0.0-beta.1` - Major version beta + +### 2. Create and Push Git Tag + +```bash +# Create tag (WITHOUT 'v' prefix) +git tag 1.0.0 + +# Push tag to trigger CI/CD +git push origin 1.0.0 +``` + +### 3. Monitor Workflow + +1. Go to your Gitea repository +2. Navigate to **Actions** tab +3. Watch the **"Publish NuGet Packages"** workflow run +4. Verify all steps complete successfully: + - ✅ Checkout code + - ✅ Setup .NET + - ✅ Build solution + - ✅ Run tests + - ✅ Pack packages + - ✅ Publish to NuGet.org + +### 4. Verify on NuGet.org + +After 5-10 minutes, check: +- https://www.nuget.org/packages/Svrnty.GeoManagement.Abstractions +- https://www.nuget.org/packages/Svrnty.GeoManagement.Google + +## What Gets Published + +Both packages are **always published together** with the same version: + +1. **Svrnty.GeoManagement.Abstractions** - Core interfaces and models +2. **Svrnty.GeoManagement.Google** - Google provider implementation + +## Workflow Details + +The Gitea workflow (`.gitea/workflows/publish-nuget.yml`): + +- **Trigger**: Any tag matching `[0-9]+.[0-9]+.[0-9]+*` pattern +- **Tag format**: Direct version number (e.g., `1.0.0`, not `v1.0.0`) +- **Quality gates**: Tests must pass before publish +- **Artifacts**: Packages uploaded to workflow artifacts for download + +## Troubleshooting + +### Workflow didn't trigger +- Verify tag format matches pattern (e.g., `1.0.0`, not `v1.0.0`) +- Check if tag was pushed: `git push origin --tags` + +### Tests failed +- Run tests locally: `dotnet test` +- Fix issues and create new tag with next patch version + +### Publish failed +- Check `NUGET_API_KEY` secret is configured in Gitea +- Verify API key has push permissions +- Check if version already exists on NuGet.org (use `--skip-duplicate`) + +### Want to unpublish +- NuGet.org doesn't allow deletion, only unlisting +- Publish new patch version with fix instead + +## Local Testing + +Test package creation before releasing: + +```bash +# Build Release configuration +dotnet build --configuration Release + +# Create packages locally +dotnet pack Svrnty.GeoManagement.Abstractions -c Release -p:Version=1.0.0-local +dotnet pack Svrnty.GeoManagement.Google -c Release -p:Version=1.0.0-local + +# Check package contents +dotnet nuget verify Svrnty.GeoManagement.Abstractions/bin/Release/*.nupkg +dotnet nuget verify Svrnty.GeoManagement.Google/bin/Release/*.nupkg +``` + +## Rollback a Release + +If you need to rollback: + +1. **Unlist the package** on NuGet.org (doesn't delete it) +2. **Publish a new patch version** with the fix +3. **Update documentation** to recommend the new version + +## Version Increment Examples + +Starting from `1.2.3`: + +- Bug fix → `1.2.4` +- New feature → `1.3.0` +- Breaking change → `2.0.0` +- Beta for next major → `2.0.0-beta.1` + +## Questions? + +- Check workflow logs in Gitea Actions tab +- Review `CLAUDE.md` for development guidance +- See `TESTING.md` for test setup diff --git a/Svrnty.GeoManagement.Abstractions/Models/Address.cs b/Svrnty.GeoManagement.Abstractions/Models/Address.cs index 6bd642e..b802c5d 100644 --- a/Svrnty.GeoManagement.Abstractions/Models/Address.cs +++ b/Svrnty.GeoManagement.Abstractions/Models/Address.cs @@ -56,6 +56,6 @@ public record Address( return $"{Line1}\n{City}, {Subdivision} {PostalCode}\n{Country}"; } - return $"{Line2}\n{Line1}\n{City}, {Subdivision} {PostalCode}\n{Country}"; + return $"{Line2}, {Line1}\n{City}, {Subdivision} {PostalCode}\n{Country}"; } } \ No newline at end of file diff --git a/Svrnty.GeoManagement.Abstractions/README.md b/Svrnty.GeoManagement.Abstractions/README.md new file mode 100644 index 0000000..94597f9 --- /dev/null +++ b/Svrnty.GeoManagement.Abstractions/README.md @@ -0,0 +1,123 @@ +# Svrnty.GeoManagement.Abstractions + +Core abstractions and models for geocoding and address management services. + +## Overview + +This package provides the foundational interfaces and data structures for implementing geocoding providers. It defines the contract for geocoding operations without being tied to any specific provider implementation. + +## Installation + +```bash +dotnet add package Svrnty.GeoManagement.Abstractions +``` + +## Key Components + +### IGeoManagementProvider Interface + +The main interface for geocoding operations: + +```csharp +public interface IGeoManagementProvider +{ + // Convert address to coordinates + Task GetGeoPointAsync(Address address, CancellationToken cancellationToken = default); + + // Convert coordinates to address + Task ReverseGeocodeAsync(GeoPoint geoPoint, CancellationToken cancellationToken = default); + + // Normalize and validate an address + Task NormalizeAddressAsync(Address address, CancellationToken cancellationToken = default); + Task NormalizeAddressAsync(string address, CancellationToken cancellationToken = default); +} +``` + +### Address Model + +Represents a structured postal address: + +```csharp +public record Address( + string Line1, + string? Line2, + string City, + string Subdivision, // State/Province + string PostalCode, + string Country, + GeoPoint? Location, + string? Note, + bool IsNormalized = false +); +``` + +Features: +- Formatted address output with `GetFormattedAddress()` method +- Support for multiple formats: Full, Compact, MultiLine +- Optional location coordinates +- Normalization tracking flag + +### GeoPoint Model + +Represents geographic coordinates: + +```csharp +public record GeoPoint(double Latitude, double Longitude); +``` + +## Usage + +This package is designed to be used with provider implementations. For example: + +```csharp +using Svrnty.GeoManagement.Abstractions.Abstractions; +using Svrnty.GeoManagement.Abstractions.Models; + +public class MyService +{ + private readonly IGeoManagementProvider _geoProvider; + + public MyService(IGeoManagementProvider geoProvider) + { + _geoProvider = geoProvider; + } + + public async Task GetCoordinates(Address address) + { + return await _geoProvider.GetGeoPointAsync(address); + } +} +``` + +## Available Providers + +- **[Svrnty.GeoManagement.Google](https://www.nuget.org/packages/Svrnty.GeoManagement.Google)** - Google Maps Geocoding API implementation + +## Implementing a Provider + +To create your own geocoding provider: + +1. Reference this package +2. Implement `IGeoManagementProvider` +3. Map provider-specific responses to `Address` and `GeoPoint` models + +```csharp +public class MyCustomProvider : IGeoManagementProvider +{ + public async Task GetGeoPointAsync(Address address, CancellationToken cancellationToken = default) + { + // Your implementation + } + + // ... implement other methods +} +``` + +## License + +MIT License - see [LICENSE](https://git.openharbor.io/svrnty/dotnet-geo-management/blob/main/LICENSE) for details + +## Links + +- [Git Repository](https://git.openharbor.io/svrnty/dotnet-geo-management) +- [Google Provider Package](https://www.nuget.org/packages/Svrnty.GeoManagement.Google) diff --git a/Svrnty.GeoManagement.Abstractions/Svrnty.GeoManagement.Abstractions.csproj b/Svrnty.GeoManagement.Abstractions/Svrnty.GeoManagement.Abstractions.csproj index fa71b7a..14f07b9 100644 --- a/Svrnty.GeoManagement.Abstractions/Svrnty.GeoManagement.Abstractions.csproj +++ b/Svrnty.GeoManagement.Abstractions/Svrnty.GeoManagement.Abstractions.csproj @@ -4,6 +4,26 @@ net8.0 enable enable + + + Svrnty.GeoManagement.Abstractions + Svrnty + Svrnty + Svrnty.GeoManagement + Core abstractions and models for geocoding and address management services. Provides interfaces and data structures for implementing geocoding providers. + MIT + https://git.openharbor.io/svrnty/dotnet-geo-management + https://git.openharbor.io/svrnty/dotnet-geo-management + git + geocoding;address;geolocation;coordinates;gps;maps;abstractions + README.md + false + true + snupkg + + + + diff --git a/Svrnty.GeoManagement.Google/Svrnty.GeoManagement.Google.csproj b/Svrnty.GeoManagement.Google/Svrnty.GeoManagement.Google.csproj index b631a3a..82c8882 100644 --- a/Svrnty.GeoManagement.Google/Svrnty.GeoManagement.Google.csproj +++ b/Svrnty.GeoManagement.Google/Svrnty.GeoManagement.Google.csproj @@ -15,6 +15,26 @@ net8.0 enable enable + + + Svrnty.GeoManagement.Google + Svrnty + Svrnty + Svrnty.GeoManagement + Google Geocoding API provider implementation for Svrnty.GeoManagement. Provides forward geocoding, reverse geocoding, and address normalization using Google Maps API. + MIT + https://git.openharbor.io/svrnty/dotnet-geo-management + https://git.openharbor.io/svrnty/dotnet-geo-management + git + geocoding;google;google-maps;address;geolocation;coordinates;gps;maps + README.md + false + true + snupkg + + + +