diff --git a/.github/workflows/dotnet-core.yml b/.github/workflows/dotnet-core.yml index d81e202..95c5149 100644 --- a/.github/workflows/dotnet-core.yml +++ b/.github/workflows/dotnet-core.yml @@ -9,7 +9,7 @@ on: branches: [ master ] env: - # Stop wasting time caching packages + # Stop wasting time caching packages DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true # Disable sending usage data to Microsoft DOTNET_CLI_TELEMETRY_OPTOUT: true @@ -20,45 +20,42 @@ env: GITHUB_USER: iancooper GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - jobs: build: - runs-on: ubuntu-latest steps: - - name: Setup dotnet - uses: actions/setup-dotnet@v4 - with: - dotnet-version: | - 8.0.x - 9.0.x - - run: dotnet --info - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: actions/cache@v4 - with: - path: ~/.nuget/packages - key: Linux-nuget-${{ hashFiles('**/Directory.Packages.props') }} - restore-keys: | - Linux-nuget- - - name: Build - run: dotnet build -c Release - - name: Core Tests - run: dotnet test -c Release --no-restore --no-build --verbosity d - - name: Upload packages as artifacts - uses: actions/upload-artifact@v4 - with: - name: nuget packages - path: "**/*.nupkg" - - name: Push to GitHub Feed - if: ${{ github.event_name != 'pull_request' }} - run: dotnet nuget push **/*.nupkg --source https://nuget.pkg.github.com/${REPOSITORY_OWNER}/index.json --api-key ${GITHUB_TOKEN} --skip-duplicate - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - REPOSITORY_OWNER: ${{ github.repository_owner }} - + - name: Setup dotnet + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 8.0.x + 9.0.x + - run: dotnet --info + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: Linux-nuget-${{ hashFiles('**/Directory.Packages.props') }} + restore-keys: | + Linux-nuget- + - name: Build + run: dotnet build ./test/Paramore.Darker.Tests/Paramore.Darker.Tests.csproj -c Release + - name: Core Tests + run: dotnet test ./test/Paramore.Darker.Tests/Paramore.Darker.Tests.csproj -c Release --no-restore --no-build --verbosity d + - name: Upload packages as artifacts + uses: actions/upload-artifact@v4 + with: + name: nuget packages + path: "**/*.nupkg" + - name: Push to GitHub Feed + if: ${{ github.event_name != 'pull_request' }} + run: dotnet nuget push **/*.nupkg --source https://nuget.pkg.github.com/${REPOSITORY_OWNER}/index.json --api-key ${GITHUB_TOKEN} --skip-duplicate + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + REPOSITORY_OWNER: ${{ github.repository_owner }} release: if: startsWith(github.ref, 'refs/tags') @@ -71,5 +68,4 @@ jobs: with: name: nuget packages - name: Push generated package to NuGet - run: dotnet nuget push **/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_KEY }} --skip-duplicate - + run: dotnet nuget push **/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_KEY }} --skip-duplicate \ No newline at end of file diff --git a/Darker.sln b/Darker.sln index 1200bda..5a6c786 100644 --- a/Darker.sln +++ b/Darker.sln @@ -7,6 +7,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution appveyor.yml = appveyor.yml Directory.Build.props = Directory.Build.props src\Directory.Build.props = src\Directory.Build.props + Directory.Packages.props = Directory.Packages.props README.md = README.md Set-Version.psm1 = Set-Version.psm1 EndProjectSection @@ -39,6 +40,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Paramore.Darker.Testing.Por EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleMinimalApi", "samples\SampleMinimalApi\SampleMinimalApi.csproj", "{F383C752-9039-494E-9545-5E2555EE9747}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Paramore.Darker.Tests.AOT", "test\Paramore.Darker.Tests.AOT\Paramore.Darker.Tests.AOT.csproj", "{9FD661C0-DE31-4FED-A98D-123B476BB3D8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleMauiTestApp", "SampleMauiTestApp\SampleMauiTestApp.csproj", "{46919655-7708-45F0-AB90-FD662ED8D4F7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Paramore.Test.Helpers", "test\Paramore.Test.Helpers\Paramore.Test.Helpers.csproj", "{134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -181,6 +188,43 @@ Global {F383C752-9039-494E-9545-5E2555EE9747}.Release|x64.Build.0 = Release|Any CPU {F383C752-9039-494E-9545-5E2555EE9747}.Release|x86.ActiveCfg = Release|Any CPU {F383C752-9039-494E-9545-5E2555EE9747}.Release|x86.Build.0 = Release|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Debug|x64.ActiveCfg = Debug|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Debug|x64.Build.0 = Debug|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Debug|x86.ActiveCfg = Debug|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Debug|x86.Build.0 = Debug|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Release|Any CPU.Build.0 = Release|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Release|x64.ActiveCfg = Release|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Release|x64.Build.0 = Release|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Release|x86.ActiveCfg = Release|Any CPU + {9FD661C0-DE31-4FED-A98D-123B476BB3D8}.Release|x86.Build.0 = Release|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Debug|x64.ActiveCfg = Debug|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Debug|x64.Build.0 = Debug|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Debug|x86.ActiveCfg = Debug|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Debug|x86.Build.0 = Debug|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Release|Any CPU.Build.0 = Release|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Release|x64.ActiveCfg = Release|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Release|x64.Build.0 = Release|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Release|x86.ActiveCfg = Release|Any CPU + {46919655-7708-45F0-AB90-FD662ED8D4F7}.Release|x86.Build.0 = Release|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Debug|x64.ActiveCfg = Debug|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Debug|x64.Build.0 = Debug|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Debug|x86.ActiveCfg = Debug|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Debug|x86.Build.0 = Debug|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Release|Any CPU.Build.0 = Release|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Release|x64.ActiveCfg = Release|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Release|x64.Build.0 = Release|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Release|x86.ActiveCfg = Release|Any CPU + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -197,6 +241,9 @@ Global {6EB80459-3AE8-4832-BD6F-F0DE5A59337B} = {1D9F464E-31FB-4275-8332-F1083911F830} {BAE5E3C9-D7A6-4C4C-8291-1141031E4215} = {1D9F464E-31FB-4275-8332-F1083911F830} {F383C752-9039-494E-9545-5E2555EE9747} = {75225A8D-3180-4D86-9D4D-0E85C8AF7810} + {9FD661C0-DE31-4FED-A98D-123B476BB3D8} = {1D9F464E-31FB-4275-8332-F1083911F830} + {46919655-7708-45F0-AB90-FD662ED8D4F7} = {75225A8D-3180-4D86-9D4D-0E85C8AF7810} + {134B22A6-D079-4FB3-5E89-2F3E6E3FE9F7} = {1D9F464E-31FB-4275-8332-F1083911F830} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {88623A3A-C685-437C-B399-435FB5B24FE9} diff --git a/Darker.sln.DotSettings b/Darker.sln.DotSettings new file mode 100644 index 0000000..0f78dd2 --- /dev/null +++ b/Darker.sln.DotSettings @@ -0,0 +1,2 @@ + + AOT \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index d927f3a..c7e0626 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -9,6 +9,9 @@ + + + diff --git a/SampleMauiTestApp/App.xaml b/SampleMauiTestApp/App.xaml new file mode 100644 index 0000000..78a3dfb --- /dev/null +++ b/SampleMauiTestApp/App.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/SampleMauiTestApp/App.xaml.cs b/SampleMauiTestApp/App.xaml.cs new file mode 100644 index 0000000..5ce8ab4 --- /dev/null +++ b/SampleMauiTestApp/App.xaml.cs @@ -0,0 +1,15 @@ +namespace SampleMauiTestApp +{ + public partial class App : Application + { + public App() + { + InitializeComponent(); + } + + protected override Window CreateWindow(IActivationState? activationState) + { + return new Window(new AppShell()); + } + } +} \ No newline at end of file diff --git a/SampleMauiTestApp/AppShell.xaml b/SampleMauiTestApp/AppShell.xaml new file mode 100644 index 0000000..af06fe8 --- /dev/null +++ b/SampleMauiTestApp/AppShell.xaml @@ -0,0 +1,15 @@ + + + + + + diff --git a/SampleMauiTestApp/AppShell.xaml.cs b/SampleMauiTestApp/AppShell.xaml.cs new file mode 100644 index 0000000..e02aa75 --- /dev/null +++ b/SampleMauiTestApp/AppShell.xaml.cs @@ -0,0 +1,10 @@ +namespace SampleMauiTestApp +{ + public partial class AppShell : Shell + { + public AppShell() + { + InitializeComponent(); + } + } +} diff --git a/SampleMauiTestApp/DarkerSettings.cs b/SampleMauiTestApp/DarkerSettings.cs new file mode 100644 index 0000000..e44f4c0 --- /dev/null +++ b/SampleMauiTestApp/DarkerSettings.cs @@ -0,0 +1,39 @@ +using Paramore.Darker.Policies; +using Polly; +using Polly.Registry; +using SampleMauiTestApp.Domain; + +namespace SampleMauiTestApp; + +public class DarkerSettings +{ + public const string SomethingWentTerriblyWrongCircuitBreakerName = "SomethingWentTerriblyWrongCircuitBreaker"; + + public static IPolicyRegistry ConfigurePolicies() + { + var defaultRetryPolicy = Policy + .Handle() + .WaitAndRetryAsync(new[] + { + TimeSpan.FromMilliseconds(50), + TimeSpan.FromMilliseconds(100), + TimeSpan.FromMilliseconds(150) + }); + + var circuitBreakerPolicy = Policy + .Handle() + .CircuitBreakerAsync(1, TimeSpan.FromMilliseconds(500)); + + var circuitBreakTheWorstCaseScenario = Policy + .Handle() + .CircuitBreakerAsync(1, TimeSpan.FromSeconds(5)); + + var policyRegistry = new PolicyRegistry + { + {Constants.RetryPolicyName, defaultRetryPolicy}, + {Constants.CircuitBreakerPolicyName, circuitBreakerPolicy}, + {SomethingWentTerriblyWrongCircuitBreakerName, circuitBreakTheWorstCaseScenario} + }; + return policyRegistry; + } +} \ No newline at end of file diff --git a/SampleMauiTestApp/Domain/PersonRepository.cs b/SampleMauiTestApp/Domain/PersonRepository.cs new file mode 100644 index 0000000..53c382b --- /dev/null +++ b/SampleMauiTestApp/Domain/PersonRepository.cs @@ -0,0 +1,45 @@ +namespace SampleMauiTestApp.Domain +{ + public sealed class PersonRepository + { + private static readonly IReadOnlyDictionary Db = new Dictionary + { + {1, "Alan Turing"}, + {2, "Donald Knuth"}, + {3, "Edsger W. Dijkstra"}, + {4, "John von Neumann"}, + {5, "Dennis Ritchie"}, + {6, "Ken Thompson"}, + {7, "Tim Berners-Lee"}, + {8, "Claude Shannon"} + }; + + public async Task> GetAll(CancellationToken cancellationToken = new()) + { + // fake some io-bound activity. Need to call ConfigureAwait(false) to avoid deadlocks in UI thread. + await FakeSomeActivity(cancellationToken).ConfigureAwait(false); + + return Db; + } + + public async Task GetNameById(int id, CancellationToken cancellationToken = new()) + { + // fake some io-bound activity. Need to call ConfigureAwait(false) to avoid deadlocks in UI thread. + await FakeSomeActivity(cancellationToken).ConfigureAwait(false); + + return Db[id]; + } + + private async Task FakeSomeActivity(CancellationToken cancellationToken = new()) + { + var random = new Random(); + + // fake some io-bound activity. Need to call ConfigureAwait(false) to avoid deadlocks in UI thread. + await Task.Delay(random.Next(50, 150), cancellationToken).ConfigureAwait(false); + + // there is a 10% chance that something goes terribly wrong + if (random.NextDouble() < 0.10) + throw new SomethingWentTerriblyWrongException(); + } + } +} \ No newline at end of file diff --git a/SampleMauiTestApp/Domain/SomethingWentTerriblyWrongException.cs b/SampleMauiTestApp/Domain/SomethingWentTerriblyWrongException.cs new file mode 100644 index 0000000..65a06c3 --- /dev/null +++ b/SampleMauiTestApp/Domain/SomethingWentTerriblyWrongException.cs @@ -0,0 +1,6 @@ +namespace SampleMauiTestApp.Domain +{ + public sealed class SomethingWentTerriblyWrongException : Exception + { + } +} \ No newline at end of file diff --git a/SampleMauiTestApp/MainPage.xaml b/SampleMauiTestApp/MainPage.xaml new file mode 100644 index 0000000..615b9be --- /dev/null +++ b/SampleMauiTestApp/MainPage.xaml @@ -0,0 +1,31 @@ + + + + + + + + +