Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
56996da
Add .github/copilot-instructions.md: guidance for AI agents
jacqueskang Dec 20, 2025
05bd265
Target net10.0 for projects previously targeting netcoreapp*
jacqueskang Dec 20, 2025
7e6876d
Update NuGet package versions (security updates) and align AWS SDKs f…
jacqueskang Dec 20, 2025
4cafff1
Remove deprecated FxCop analyzers; rely on built-in SDK analyzers
jacqueskang Dec 20, 2025
de604e0
CI: add GitHub Actions PR validation workflow (restore/build/test)
jacqueskang Dec 20, 2025
5bb0b53
ci: remove Azure DevOps PR validation pipeline (migrated to GitHub Ac…
jacqueskang Dec 20, 2025
5bf4aa6
ci: add diagnostic step to PR validation workflow (dotnet info + verb…
jacqueskang Dec 20, 2025
213ec60
CI: avoid solution-level 'dotnet test' to work around VSTest absolute…
jacqueskang Dec 20, 2025
a239ff5
ci: make diagnostic step cross-platform (fix PowerShell ls flags)
jacqueskang Dec 20, 2025
0ffb4ef
ci: fix Windows diagnostic step (remove stray pipe)
jacqueskang Dec 20, 2025
283ca4b
ci: run tests using explicit csproj and save TRX results
jacqueskang Dec 20, 2025
30ef23e
ci: bump Microsoft.NET.Test.Sdk to 18.2.0 to avoid VSTest path parsin…
jacqueskang Dec 20, 2025
4d6ed71
ci: run tests by globbed csproj loop, produce per-project TRX
jacqueskang Dec 20, 2025
d0dfe17
feat: rollback test sdk
jacqueskang Dec 20, 2025
d224969
fix: build with debug config
jacqueskang Dec 20, 2025
23b12cb
feat: migrate to github action for pr validation
jacqueskang Dec 20, 2025
7c58436
Migrate CI to GitHub Actions; publish NuGet; enforce version.yml in P…
jacqueskang Dec 20, 2025
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
67 changes: 67 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copilot instructions — EventSourcing (JKang.EventSourcing)

Purpose: help an AI coding agent quickly be productive in this repo — what matters, where to look, and how to run/verify work.

## Quick summary
- This is a .NET Core event-sourcing framework (see `README.md`).
- Core projects: `JKang.EventSourcing.*` (abstractions, options, snapshotting, persistence implementations).
- Persistence providers are separate projects under `src/` (e.g., `Persistence.FileSystem`, `Persistence.DynamoDB`, `Persistence.CosmosDB`, `Persistence.EfCore`, `Persistence.S3`).
- `JKang.EventSourcing.TestingWebApp` is an interactive sample app to exercise different persistence backends.

## Useful commands (local dev)
- Restore/build all: `dotnet restore` then `dotnet build src\EventSourcing.sln`
- Run tests: `dotnet test src` (or `dotnet test` inside a specific `*.Tests` project)
- Run the sample web app: `dotnet run --project src\JKang.EventSourcing.TestingWebApp\JKang.EventSourcing.TestingWebApp.csproj`
- Toggle storage backend inside `Startup.cs` (see `PersistenceMode` and `ConfigureServicesFor*` methods) or set `appsettings.json` values for AWS/Cosmos.
- CI: Azure Pipelines YAML lives under `build/` (e.g., `azure-pipeline.yml`, `pack-publish.yml`).

## Architecture & important files to read first
- High-level: core abstractions and patterns are in `JKang.EventSourcing.Abstractions` and `JKang.EventSourcing`.
- Examples & canonical patterns: `src\JKang.EventSourcing.TestingFixtures\` (e.g., `GiftCard.cs`, `GiftCardSnapshot.cs`, `GiftCardRepository.cs`) and `samples/`.
- Persistence pattern: each store provides
- Event store (`IEventStore<TAgg,TKey>` implementation) — e.g., `TextFileEventStore`, `DynamoDBEventStore`, `EfCoreEventStore`.
- Snapshot store (`ISnapshotStore<TAgg,TKey>`) when supported.
- Builder extension to wire into DI: `*PersistenceEventSourcingBuilderExtensions.cs` (e.g., `S3SnapshotPersistenceEventSourcingBuilderExtensions.cs`).
- `Defaults.cs` with Json.NET settings used consistently across providers.
- Initializers: `IEventStoreInitializer<>` / `ISnapshotStoreInitializer<>` — used by `TestingWebApp` to create tables/containers before use.
- Serializers: Cosmos uses `EventSourcingCosmosSerializer` and persistence uses `Defaults.JsonSerializerSettings`. Ensure any new event types are JSON-serializable with these settings.

## Project-specific conventions & patterns
- Aggregate pattern: aggregates implement `IAggregate<TKey>` (often via `Aggregate<TKey>`) and mutate state only by applying `IAggregateEvent<TKey>` in `ApplyEvent`.
- Required constructors: `(TKey id, IEnumerable<IAggregateEvent<TKey>> savedEvents)` and `(TKey id, IAggregateSnapshot<TKey> snapshot, IEnumerable<IAggregateEvent<TKey>> savedEvents)` for rehydration.
- Snapshot support via `TakeSnapshot()` and `IAggregateSnapshot`.
- Events should be immutable and JSON-serializable. Many stores call `JsonConvert.SerializeObject(@event, Defaults.JsonSerializerSettings)`.
- Naming: options & configuration types end with `Options` (e.g., `S3SnapshotStoreOptions`), and extension classes follow the `*PersistenceEventSourcingBuilderExtensions` pattern.
- Async-first: APIs and store methods use `async`/`Task` and take optional `CancellationToken`.

## How to add a new persistence provider (pattern to follow)
1. Create `JKang.EventSourcing.Persistence.YourProvider` project under `src/`.
2. Add `Defaults.cs` for serializer settings (reuse pattern from existing `Defaults.cs`).
3. Implement `IEventStore<TAggregate,TKey>` and (optionally) `ISnapshotStore<TAggregate,TKey>`.
4. Provide builder extension `YourProviderPersistenceEventSourcingBuilderExtensions.cs` that:
- Adds configuration via `.ConfigureAggregate<TAggregate,TKey,YourOptions>(...)` and registers the services in DI.
5. Add initializer implementing `IEventStoreInitializer<>` / `ISnapshotStoreInitializer<>` if the backend needs resource creation (tables, containers, buckets).
6. Create tests mirroring existing provider tests (see `*.Tests` projects) and a sample configuration in `TestingWebApp`.

## Testing & local integration tips
- Unit tests use xUnit (see `*.Tests` projects under `src/`). Use `dotnet test`.
- DynamoDB local: `TestingWebApp` uses `ServiceURL = "http://localhost:8800"` under DEBUG — useful with a local DynamoDB container (e.g., LocalStack or dynamodb-local).
- Cosmos: `ConfigureServicesForCosmosDB` expects a connection string under `ConnectionStrings:CosmosDB` (see `Startup.cs`).
- S3 and DynamoDB integration tests assume AWS credentials or local endpoints configured in environment or `appsettings.json`.

## What to check when changing behavior
- Verify serialization round-trip: events and snapshots must deserialize correctly with `Defaults.JsonSerializerSettings` (or `EventSourcingCosmosSerializer` for Cosmos).
- Make sure aggregate invariants are enforced in `ApplyEvent` or when emitting events (tests often assert that invalid ops throw `InvalidOperationException`).
- Update `TestingWebApp` to allow manual verification of a new storage configuration.

## Where to look for examples / canonical code
- Aggregate + events: `src\JKang.EventSourcing.TestingFixtures\GiftCard.cs`, `GiftCardCreated.cs`, `GiftCardDebited.cs`.
- Text file provider: `src\JKang.EventSourcing.Persistence.FileSystem\` (simple, readable reference implementation).
- EF Core provider + snapshots: `src\JKang.EventSourcing.Persistence.EfCore\`.
- DynamoDB provider: `src\JKang.EventSourcing.Persistence.DynamoDB\` (good example for non-relational storage patterns).
- Testing web app: `src\JKang.EventSourcing.TestingWebApp\Startup.cs` (how to wire different providers at runtime).

---
If you want, I can merge this into any existing `.github/copilot-instructions.md` (if present) or iterate with more details (e.g., example PR checklist, coding style rules, or common bug patterns). Please tell me which additions you'd like.

*Generated: concise, practical, and based on discoverable patterns in this repo.*
85 changes: 85 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: CI Build & Pack

on:
push:
branches:
- develop
workflow_dispatch: {}

jobs:
build-and-pack:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: nuget-packages-${{ runner.os }}-$(sha1sum version.yml | cut -d' ' -f1)
restore-keys: |
nuget-packages-${{ runner.os }}-

- name: Setup .NET SDK
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Determine package version
id: pkgver
run: |
VERSION_LINE=$(grep '^ version:' version.yml || true)
VERSION=$(echo $VERSION_LINE | awk '{print $2}')
if [ -z "$VERSION" ]; then
echo "version not found in version.yml, defaulting to 0.0.0"
VERSION=0.0.0
fi
# Append run number so CI packages are unique
PACKAGE_VERSION="${VERSION}-ci.${GITHUB_RUN_NUMBER}"
echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV
echo "Determined PACKAGE_VERSION=$PACKAGE_VERSION"

- name: Restore
run: dotnet restore

- name: Build
run: dotnet build --configuration Release --no-restore

- name: Run tests
run: dotnet test --configuration Release --no-build --verbosity normal || true

- name: Pack NuGet packages
run: |
mkdir -p artifacts
dotnet pack "src/**/*.csproj" -c Release -o artifacts /p:PackageVersion=${{ env.PACKAGE_VERSION }} --no-build

- name: List artifacts
run: ls -la artifacts || true

- name: Upload NuGet packages
uses: actions/upload-artifact@v4
with:
name: nuget-packages
path: artifacts/*.nupkg

- name: Publish to NuGet.org
if: ${{ github.ref == 'refs/heads/develop' }}
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
set -euo pipefail
echo "Publishing packages to NuGet.org"
shopt -s nullglob || true
files=(artifacts/*.nupkg)
if [ ${#files[@]} -eq 0 ]; then
echo "No packages found, skipping publish"
exit 0
fi
for pkg in "${files[@]}"; do
echo "Pushing $pkg"
dotnet nuget push "$pkg" -s https://api.nuget.org/v3/index.json -k "$NUGET_API_KEY" --skip-duplicate
done
66 changes: 66 additions & 0 deletions .github/workflows/pr-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: PR Validation (Minimal)

on:
pull_request:
branches:
- '**'

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Require version.yml change
if: ${{ github.event_name == 'pull_request' }}
run: |
set -euo pipefail
echo "Checking that version.yml was modified in this PR..."
# Fetch base branch to compare
git fetch origin ${{ github.event.pull_request.base.ref }} --depth=1
changed_files=$(git diff --name-only origin/${{ github.event.pull_request.base.ref }}...HEAD || true)
echo "$changed_files"
if ! echo "$changed_files" | grep -xq 'version.yml'; then
echo "::error::Pull request must include an updated version.yml"
exit 1
fi

- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: '10.0.x'

- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/*.csproj') }}
restore-keys: |
nuget-${{ runner.os }}-

- name: Restore
run: dotnet restore src/EventSourcing.sln

- name: Build
run: dotnet build src/EventSourcing.sln --no-restore

- name: Test - all
run: |
set -euo pipefail
mkdir -p TestResults
for proj in src/*Tests/*.csproj; do
echo "Running tests for $proj"
name=$(basename "$proj" .csproj)
dotnet test "$proj" --no-build --logger "trx;LogFileName=${name}.trx" --results-directory TestResults
done

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results
path: TestResults

24 changes: 0 additions & 24 deletions build/azure-pipeline-ci.yml

This file was deleted.

24 changes: 0 additions & 24 deletions build/azure-pipeline-pr.yml

This file was deleted.

54 changes: 0 additions & 54 deletions build/azure-pipeline.yml

This file was deleted.

19 changes: 0 additions & 19 deletions build/templates/build-test.yml

This file was deleted.

26 changes: 0 additions & 26 deletions build/templates/pack-publish.yml

This file was deleted.

2 changes: 0 additions & 2 deletions build/version.yml

This file was deleted.

Loading