-
Notifications
You must be signed in to change notification settings - Fork 1
pin the public api, build every package in ci, stop packing the examples #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,122 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| push: | ||
| branches: [master] | ||
| pull_request: | ||
| branches: [master] | ||
|
|
||
| concurrency: | ||
| group: ci-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| # Read-only. Nothing here pushes, tags or releases, so the default write-capable token is | ||
| # more authority than the job needs. | ||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| build: | ||
| name: build | ||
| runs-on: ubuntu-latest | ||
| # The suite is slow for reasons tracked in #11: one test uses 100 retries with no base delay, | ||
| # so jitter saturates toward the 30 second cap and it alone accounts for most of the runtime. | ||
| # Generous enough not to flake, tight enough that a hang is not a 6 hour job. | ||
| timeout-minutes: 30 | ||
|
|
||
| steps: | ||
| # persist-credentials: false because no step runs an authenticated git or gh command. | ||
| # Left on, checkout writes the token into .git/config where any later step, including | ||
| # anything a dependency pulls in, can read it. | ||
| - uses: actions/checkout@v4 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - uses: actions/setup-dotnet@v4 | ||
| with: | ||
| dotnet-version: 8.0.x | ||
|
|
||
| - run: dotnet restore Carom.sln | ||
|
|
||
| # Release, because Release is what ships. Testing Debug while packing Release means the | ||
| # assembly consumers install has never had a test run against it. | ||
| - run: dotnet build Carom.sln --configuration Release --no-restore | ||
|
|
||
| # No LogFileName. A solution with four test projects runs four assemblies, and a fixed | ||
| # name makes each one overwrite the last in the shared results directory, so only the | ||
| # final assembly's trx survives. The step below then counts 196 of 295 and, worse, would | ||
| # have counted a green run as complete if the floor happened to be lower. | ||
| - name: Test | ||
| run: > | ||
| dotnet test Carom.sln | ||
| --configuration Release | ||
| --no-build | ||
| --logger "trx" | ||
| --results-directory ${{ github.workspace }}/testresults | ||
| --verbosity normal | ||
|
|
||
| # A run that discovers no tests exits zero, so the whole job can go green while proving | ||
| # nothing. Assert a floor and that everything found actually passed. | ||
| - name: Confirm tests actually ran | ||
| run: | | ||
| TOTAL=0 | ||
| PASSED=0 | ||
| for TRX in $(find "${{ github.workspace }}/testresults" -name '*.trx'); do | ||
| t=$(grep -o 'total="[0-9]*"' "$TRX" | head -1 | grep -o '[0-9]*') | ||
| p=$(grep -o 'passed="[0-9]*"' "$TRX" | head -1 | grep -o '[0-9]*') | ||
| TOTAL=$((TOTAL + ${t:-0})) | ||
| PASSED=$((PASSED + ${p:-0})) | ||
| done | ||
| echo "total=$TOTAL passed=$PASSED" | ||
| test "${TOTAL:-0}" -ge 250 || { echo "::error::Expected at least 250 tests, got ${TOTAL:-0}"; exit 1; } | ||
| test "$TOTAL" = "$PASSED" || { echo "::error::Not every test passed"; exit 1; } | ||
|
|
||
| - uses: actions/upload-artifact@v4 | ||
| if: always() | ||
| with: | ||
| name: testresults | ||
| path: ${{ github.workspace }}/testresults | ||
|
|
||
| # Packs every packable project so a broken pack is a pull request failure rather than a | ||
| # surprise on release day. Three of these were absent from Carom.sln until this branch, so | ||
| # they were published while never being built by CI at all. | ||
| - name: Pack | ||
| run: dotnet pack Carom.sln --configuration Release --no-build --output ./artifacts | ||
|
|
||
| # `ls -la` was diagnostic, not a check: it succeeds whenever any package exists, so a | ||
| # missing package or a newly-packable example would both have passed. The point of this | ||
| # branch is that the set is exactly right, so assert the set. | ||
| - name: Verify the package set | ||
| shell: bash | ||
| run: | | ||
| set -uo pipefail | ||
| expected="Carom Carom.AspNetCore Carom.DependencyInjection Carom.EntityFramework Carom.Extensions Carom.Http Carom.Telemetry.OpenTelemetry" | ||
|
|
||
| # Strip the version and the .nupkg to get the package id. Symbol packages are excluded | ||
| # so a .snupkg alongside a .nupkg is not counted as a second package. | ||
| actual=$(find ./artifacts -maxdepth 1 -name '*.nupkg' ! -name '*.symbols.nupkg' -exec basename {} \; \ | ||
| | sed -E 's/\.[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?\.nupkg$//' | sort -u) | ||
|
|
||
| echo "packed:"; printf ' %s\n' $actual | ||
|
|
||
| fail=0 | ||
| for want in $expected; do | ||
| printf '%s\n' "$actual" | grep -Fxq "$want" || { echo "::error::Missing package: $want"; fail=1; } | ||
| done | ||
|
|
||
| # Named explicitly rather than inferred. The example is IsPackable=false today, and this | ||
| # is what notices the day somebody removes that line. | ||
| if printf '%s\n' "$actual" | grep -Fxq "Carom.Examples.WebApi"; then | ||
| echo "::error::Carom.Examples.WebApi was packed. The example must not ship as a package." | ||
| fail=1 | ||
| fi | ||
|
|
||
| count=$(printf '%s\n' "$actual" | grep -c . || true) | ||
| want_count=$(printf '%s\n' $expected | grep -c .) | ||
| if [ "$count" -ne "$want_count" ]; then | ||
| echo "::error::Expected exactly $want_count packages, found $count. A new packable project needs adding to this list." | ||
| fail=1 | ||
| fi | ||
|
|
||
| test "$fail" -eq 0 || exit 1 | ||
| echo "Package set is exactly right." | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
tests/Carom.ApiApproval.Tests/ApprovedApi/Carom.AspNetCore.approved.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| namespace Carom.AspNetCore | ||
| { | ||
| public sealed class CaromCircuitBreakerHealthCheck : Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck | ||
| { | ||
| public CaromCircuitBreakerHealthCheck(string serviceName) { } | ||
| public System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default) { } | ||
| } | ||
| public class CaromHealthCheck : Microsoft.Extensions.Diagnostics.HealthChecks.IHealthCheck | ||
| { | ||
| public CaromHealthCheck(string name, System.Func<System.Threading.Tasks.Task<bool>> healthCheckFunc) { } | ||
| public System.Threading.Tasks.Task<Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckResult> CheckHealthAsync(Microsoft.Extensions.Diagnostics.HealthChecks.HealthCheckContext context, System.Threading.CancellationToken cancellationToken = default) { } | ||
| } | ||
| public static class CaromServiceCollectionExtensions | ||
| { | ||
| public static Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder AddCaromCircuitBreaker(this Microsoft.Extensions.DependencyInjection.IHealthChecksBuilder builder, string serviceName, string? name = null, Microsoft.Extensions.Diagnostics.HealthChecks.HealthStatus? failureStatus = default, string[]? tags = null) { } | ||
| } | ||
| } |
56 changes: 56 additions & 0 deletions
56
tests/Carom.ApiApproval.Tests/ApprovedApi/Carom.DependencyInjection.approved.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| namespace Carom.DependencyInjection | ||
| { | ||
| public static class CaromServiceCollectionExtensions | ||
| { | ||
| public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddCaromResilience(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Carom.DependencyInjection.IResiliencePipelineConfigurator> configureAll) { } | ||
| public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddCaromResilience(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, string name, System.Action<Carom.DependencyInjection.ResiliencePipelineBuilder> configure) { } | ||
| public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddCaromResilienceRegistry(this Microsoft.Extensions.DependencyInjection.IServiceCollection services) { } | ||
| } | ||
| public interface IResiliencePipelineConfigurator | ||
| { | ||
| Carom.DependencyInjection.IResiliencePipelineConfigurator AddPipeline(string name, System.Action<Carom.DependencyInjection.ResiliencePipelineBuilder> configure); | ||
| } | ||
| public interface IResiliencePipelineRegistry | ||
| { | ||
| Carom.DependencyInjection.ResiliencePipeline GetPipeline(string name); | ||
| bool TryGetPipeline(string name, out Carom.DependencyInjection.ResiliencePipeline? pipeline); | ||
| } | ||
| public interface IResilienceStrategy | ||
| { | ||
| T Execute<T>(System.Func<T> action); | ||
| System.Threading.Tasks.Task<T> ExecuteAsync<T>(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<T>> action, System.Threading.CancellationToken ct); | ||
| } | ||
| public class ResiliencePipeline | ||
| { | ||
| public string Name { get; } | ||
| public void Execute(System.Action action) { } | ||
| public T Execute<T>(System.Func<T> action) { } | ||
| public System.Threading.Tasks.Task ExecuteAsync(System.Func<System.Threading.Tasks.Task> action, System.Threading.CancellationToken ct = default) { } | ||
| public System.Threading.Tasks.Task ExecuteAsync(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task> action, System.Threading.CancellationToken ct = default) { } | ||
| public System.Threading.Tasks.Task<T> ExecuteAsync<T>(System.Func<System.Threading.Tasks.Task<T>> action, System.Threading.CancellationToken ct = default) { } | ||
| public System.Threading.Tasks.Task<T> ExecuteAsync<T>(System.Func<System.Threading.CancellationToken, System.Threading.Tasks.Task<T>> action, System.Threading.CancellationToken ct = default) { } | ||
| } | ||
| public class ResiliencePipelineBuilder | ||
| { | ||
| public ResiliencePipelineBuilder(string name) { } | ||
| public Carom.DependencyInjection.ResiliencePipelineBuilder AddBulkhead(string resourceKey, Carom.Extensions.Compartment config) { } | ||
| public Carom.DependencyInjection.ResiliencePipelineBuilder AddBulkhead(string resourceKey, int maxConcurrency, int queueDepth = 0) { } | ||
| public Carom.DependencyInjection.ResiliencePipelineBuilder AddCircuitBreaker(string serviceKey, Carom.Extensions.Cushion config) { } | ||
| public Carom.DependencyInjection.ResiliencePipelineBuilder AddCircuitBreaker(string serviceKey, int failureThreshold, int samplingWindow) { } | ||
| public Carom.DependencyInjection.ResiliencePipelineBuilder AddFallback<TResult>(System.Func<System.Exception, TResult> fallback) { } | ||
| public Carom.DependencyInjection.ResiliencePipelineBuilder AddFallback<TResult>(TResult fallbackValue) { } | ||
| public Carom.DependencyInjection.ResiliencePipelineBuilder AddRateLimit(string serviceKey, Carom.Extensions.Throttle config) { } | ||
| public Carom.DependencyInjection.ResiliencePipelineBuilder AddRateLimit(string serviceKey, int maxRequests, System.TimeSpan timeWindow) { } | ||
| public Carom.DependencyInjection.ResiliencePipelineBuilder AddRetry(Carom.Bounce config) { } | ||
| public Carom.DependencyInjection.ResiliencePipelineBuilder AddRetry(int retries = 3) { } | ||
| public Carom.DependencyInjection.ResiliencePipelineBuilder AddTimeout(System.TimeSpan timeout) { } | ||
| public Carom.DependencyInjection.ResiliencePipeline Build() { } | ||
| } | ||
| public class ResiliencePipelineRegistry : Carom.DependencyInjection.IResiliencePipelineRegistry | ||
| { | ||
| public ResiliencePipelineRegistry() { } | ||
| public Carom.DependencyInjection.ResiliencePipeline GetPipeline(string name) { } | ||
| public void Register(string name, Carom.DependencyInjection.ResiliencePipeline pipeline) { } | ||
| public bool TryGetPipeline(string name, out Carom.DependencyInjection.ResiliencePipeline? pipeline) { } | ||
| } | ||
| } |
9 changes: 9 additions & 0 deletions
9
tests/Carom.ApiApproval.Tests/ApprovedApi/Carom.EntityFramework.approved.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| namespace Carom.EntityFramework | ||
| { | ||
| public static class CaromDbContextExtensions | ||
| { | ||
| public static System.Threading.Tasks.Task<T> ExecuteWithRetryAsync<T>(this Microsoft.EntityFrameworkCore.DbContext context, System.Func<System.Threading.Tasks.Task<T>> operation, int retries = 3, System.Threading.CancellationToken cancellationToken = default) { } | ||
| public static System.Threading.Tasks.Task<int> SaveChangesWithRetryAsync(this Microsoft.EntityFrameworkCore.DbContext context, Carom.Bounce bounce, System.Threading.CancellationToken cancellationToken = default) { } | ||
| public static System.Threading.Tasks.Task<int> SaveChangesWithRetryAsync(this Microsoft.EntityFrameworkCore.DbContext context, int retries = 3, System.Threading.CancellationToken cancellationToken = default) { } | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.