Skip to content
Closed

otel #12

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
18 changes: 18 additions & 0 deletions .github/actions/setup-dotnet/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Setup .NET
description: Install the .NET SDK (pinned via global.json) and restore the NuGet package cache.

runs:
using: composite
steps:
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
global-json-file: global.json

- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props') }}
restore-keys: |
${{ runner.os }}-nuget-
65 changes: 27 additions & 38 deletions .github/workflows/dotnet.publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ name: .NET Publish
on:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: "Version to pack for a dry run (build/test/pack/upload only — no nuget push)"
required: false
default: "0.0.0-dryrun"

concurrency:
group: publish-${{ github.event.release.tag_name || github.run_id }}
cancel-in-progress: false

permissions:
contents: read
Expand All @@ -11,68 +21,44 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props') }}
restore-keys: |
${{ runner.os }}-nuget-
- uses: actions/checkout@v7
- uses: ./.github/actions/setup-dotnet

- name: Restore dependencies
run: dotnet restore

- name: Build
run: dotnet build -c Debug --no-restore
run: dotnet build -c Release --no-restore

- name: Test
run: dotnet test --no-build
run: dotnet test -c Release --no-build

publish:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props') }}
restore-keys: |
${{ runner.os }}-nuget-
- uses: actions/checkout@v7
- uses: ./.github/actions/setup-dotnet

- name: Resolve package version
env:
EVENT: ${{ github.event_name }}
TAG: ${{ github.event.release.tag_name }}
INPUT_VERSION: ${{ inputs.version }}
run: |
version="${TAG#v}"
if [ "$EVENT" = "release" ]; then version="${TAG#v}"; else version="$INPUT_VERSION"; fi
[[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]] \
|| { echo "::error::invalid version '$version' (event '$EVENT')"; exit 1; }
echo "Publishing version $version"
echo "PACKAGE_VERSION=$version" >> "$GITHUB_ENV"

- name: Restore
run: dotnet restore

- name: Pack Plumber
run: dotnet pack src/Plumber -c Release --no-restore -o nuget -p:PackageVersion=$PACKAGE_VERSION -p:Version=$PACKAGE_VERSION

- name: Pack Plumber.Testing
run: dotnet pack src/Plumber.Testing -c Release --no-restore -o nuget -p:PackageVersion=$PACKAGE_VERSION -p:Version=$PACKAGE_VERSION

- name: Pack Plumber.Serilog.Extensions
run: dotnet pack src/Plumber.Serilog.Extensions -c Release --no-restore -o nuget -p:PackageVersion=$PACKAGE_VERSION -p:Version=$PACKAGE_VERSION
- name: Pack
run: dotnet pack Plumber.slnx -c Release --no-restore -o nuget -p:PackageVersion=$PACKAGE_VERSION -p:Version=$PACKAGE_VERSION

- name: Upload package artifacts
uses: actions/upload-artifact@v7
Expand All @@ -82,4 +68,7 @@ jobs:
retention-days: 30

- name: Publish to api.nuget.org
run: dotnet nuget push nuget/*.nupkg --skip-duplicate -k ${{ secrets.NUGET_API_KEY }} -s https://api.nuget.org/v3/index.json
if: github.event_name == 'release'
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: dotnet nuget push nuget/*.nupkg --skip-duplicate -k "$NUGET_API_KEY" -s https://api.nuget.org/v3/index.json
37 changes: 9 additions & 28 deletions .github/workflows/dotnet.tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ name: .NET Tests
on:
push:
branches: [ "main" ]
paths: [ "src/**", "tests/**", "samples/**", "Directory.Build.props", "Directory.Build.targets", "Directory.Packages.props", ".editorconfig", ".gitattributes", ".github/workflows/dotnet.tests.yml" ]
paths: [ "src/**", "tests/**", "samples/**", "Directory.Build.props", "Directory.Build.targets", "Directory.Packages.props", ".editorconfig", ".gitattributes", "global.json", ".github/workflows/dotnet.tests.yml", ".github/actions/setup-dotnet/action.yml" ]
pull_request:
branches: [ "main" ]
paths: [ "src/**", "tests/**", "samples/**", "Directory.Build.props", "Directory.Build.targets", "Directory.Packages.props", ".editorconfig", ".gitattributes", ".github/workflows/dotnet.tests.yml" ]
paths: [ "src/**", "tests/**", "samples/**", "Directory.Build.props", "Directory.Build.targets", "Directory.Packages.props", ".editorconfig", ".gitattributes", "global.json", ".github/workflows/dotnet.tests.yml", ".github/actions/setup-dotnet/action.yml" ]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -19,19 +19,8 @@ jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props') }}
restore-keys: |
${{ runner.os }}-nuget-
- uses: actions/checkout@v7
- uses: ./.github/actions/setup-dotnet

- name: Verify formatting
run: dotnet format "Plumber.slnx" --severity info --verify-no-changes
Expand All @@ -42,19 +31,8 @@ jobs:
matrix:
configuration: [ Debug, Release ]
steps:
- uses: actions/checkout@v6
- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: 10.0.x

- name: Cache NuGet packages
uses: actions/cache@v5
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', 'Directory.Packages.props') }}
restore-keys: |
${{ runner.os }}-nuget-
- uses: actions/checkout@v7
- uses: ./.github/actions/setup-dotnet

- name: Restore dependencies
run: dotnet restore
Expand All @@ -72,5 +50,8 @@ jobs:
name: coverage-cobertura
path: |
tests/Plumber.Tests/coverage.cobertura.xml
tests/Plumber.Testing.Tests/coverage.cobertura.xml
tests/Plumber.Diagnostics.Tests/coverage.cobertura.xml
tests/Plumber.Serilog.Extensions.Tests/coverage.cobertura.xml
samples/Sample.Cli.Tests/coverage.cobertura.xml
if-no-files-found: error
7 changes: 6 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

<PropertyGroup Condition="'$(GITHUB_ACTIONS)' == 'true'">
<!-- Reproducible builds + normalized SourceLink paths in packed binaries. On only in CI. -->
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>

<PropertyGroup>
<!-- CA1308: ToUpperInvariant over ToLowerInvariant is a Turkic-locale normalization rule; this codebase does not use the result for security/identity comparisons -->
<!-- CA2007: ConfigureAwait is windows-only advice; Plumber is a library -->
Expand All @@ -26,7 +31,7 @@
<CollectCoverage>true</CollectCoverage>
<CoverletOutputFormat>cobertura</CoverletOutputFormat>
<!-- Solution-wide coverage ratchet. Raise as coverage grows; never lower. Per-project overrides allowed when a module is intentionally below the bar. -->
<Threshold>95,91,95</Threshold>
<Threshold>95,95,95</Threshold>
<ThresholdType>line,branch,method</ThresholdType>
<ThresholdStat>minimum</ThresholdStat>
<!-- Exception types are plumbing; exclude from coverage to avoid noise. -->
Expand Down
7 changes: 6 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,16 @@
<PackageVersion Include="Serilog.Formatting.Compact" Version="3.0.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.1.1" />

<!-- OpenTelemetry SDK for the sample (the library instruments via the BCL ActivitySource/Meter
and does not reference these; collecting/exporting telemetry is a consumer concern) -->
<PackageVersion Include="OpenTelemetry" Version="1.16.0" />
<PackageVersion Include="OpenTelemetry.Exporter.InMemory" Version="1.16.0" />

<!-- Test infrastructure -->
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
<PackageVersion Include="coverlet.msbuild" Version="10.0.1" />
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.7.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.6.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
<PackageVersion Include="xunit.v3" Version="3.2.2" />
<!-- Granular xunit.v3 libs for the shared arch-test base (a class library, not an executable test project) -->
<PackageVersion Include="xunit.v3.extensibility.core" Version="3.2.2" />
Expand Down
2 changes: 2 additions & 0 deletions Plumber.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
<File Path=".github/workflows/dotnet.tests.yml" />
</Folder>
<Folder Name="/src/">
<Project Path="src/Plumber.Diagnostics/Plumber.Diagnostics.csproj" />
<Project Path="src/Plumber.Serilog.Extensions/Plumber.Serilog.Extensions.csproj" />
</Folder>
<Folder Name="/tests/">
<Project Path="tests/Architecture.Testing/Architecture.Testing.csproj" />
<Project Path="tests/Plumber.Diagnostics.Tests/Plumber.Diagnostics.Tests.csproj" />
<Project Path="tests/Plumber.Serilog.Extensions.Tests/Plumber.Serilog.Extensions.Tests.csproj" />
</Folder>
<Project Path="samples/Sample.Cli.Tests/Sample.Cli.Tests.csproj" />
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

*Another weapon from the MSL Armory*

Middleware pipelines for host-free .NET projects. The same shape ASP.NET Core gives web apps — request, response, a chain of steps with DI and configuration — for console apps, AWS Lambdas, Azure Functions, queue consumers, file processors, and anything else that lives outside a host.
Middleware pipelines for host-free .NET projects. Plumber brings the ASP.NET Core middleware model — a request, a response, and a chain of steps with dependency injection and configuration — to .NET projects that run without a host: console apps, AWS Lambdas, Azure Functions, queue consumers, file processors, and similar workloads.

The [wiki](https://github.com/marklauter/plumber/wiki) is the full documentation: concepts, a tutorial, per-type reference, and deployment recipes.

Expand All @@ -19,7 +19,7 @@ The [wiki](https://github.com/marklauter/plumber/wiki) is the full documentation
- **`MSL.Plumber.Pipeline`** — the core builder, handler, middleware, and request context. See [Pipeline](https://github.com/marklauter/plumber/wiki/Building-A-Pipeline).
- **`MSL.Plumber.Pipeline.Testing`** — `PlumberApplicationFactory` for exercising a real pipeline in tests. See [Testing](https://github.com/marklauter/plumber/wiki/Testing).
- **`MSL.Plumber.Serilog.Extensions`** — per-request Serilog request logging. See [Serilog Extensions](https://github.com/marklauter/plumber/wiki/Serilog-Extensions).
- **`MSL.Plumber.OpenTelemetry.Extensions`** — tracing and metrics middleware. *Planned.*
- **`MSL.Plumber.Diagnostics`** — per-request OpenTelemetry tracing and metrics middleware. See [Diagnostics](https://github.com/marklauter/plumber/wiki/Diagnostics).

## Install

Expand Down Expand Up @@ -48,14 +48,14 @@ var greeting = await handler.InvokeAsync("World");
Console.WriteLine(greeting); // Hello, World!
```

That's the whole shape: a builder, a built handler, one or more middleware, and an `InvokeAsync` call. Each invocation gets its own DI scope and cancellation token.
A Plumber application has four pieces: a builder, the handler it builds, one or more middleware, and an `InvokeAsync` call. Each invocation gets its own dependency injection scope and cancellation token.

## Where to go next

- **New to middleware pipelines?** Start with [Concepts](https://github.com/marklauter/plumber/wiki/Concepts), then the [Tutorial](https://github.com/marklauter/plumber/wiki/Tutorial).
- **Know the shape already?** Jump into [Building a pipeline](https://github.com/marklauter/plumber/wiki/Building-A-Pipeline), [Middleware](https://github.com/marklauter/plumber/wiki/Middleware), and [Request lifecycle](https://github.com/marklauter/plumber/wiki/Request-Lifecycle).
- **Looking for a specific scenario?** Browse the recipes — AWS Lambda, Azure Functions, queue consumers, webhooks, and more — from the [wiki home](https://github.com/marklauter/plumber/wiki).
- **Migrating from v2 or v3?** See [Migration](https://github.com/marklauter/plumber/wiki/Migration).
- **Migrating from an earlier version?** See [Migration](https://github.com/marklauter/plumber/wiki/Migration). v5 makes no breaking changes; v4 and v3 are covered with before-and-after examples.

---
[Repository](https://github.com/marklauter/plumber) · [NuGet — Pipeline](https://www.nuget.org/packages/MSL.Plumber.Pipeline/) · [NuGet — Testing](https://www.nuget.org/packages/MSL.Plumber.Pipeline.Testing/) · [MIT License](https://github.com/marklauter/plumber/blob/main/LICENSE) · [Report an issue](https://github.com/marklauter/plumber/issues)
Loading
Loading