diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 2a953e7df0..57dfb02004 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -170,13 +170,13 @@
-
+
https://github.com/dotnet/arcade
- 67d23f4ba1813b315e7e33c71d18b63475f5c5f8
+ e6f70c7dd528f05cd28cec2a179d58c22e91d9ac
-
+
https://github.com/dotnet/arcade
- 67d23f4ba1813b315e7e33c71d18b63475f5c5f8
+ e6f70c7dd528f05cd28cec2a179d58c22e91d9ac
diff --git a/eng/common/templates-official/job/source-index-stage1.yml b/eng/common/templates-official/job/source-index-stage1.yml
index f0513aee5b..43ee0c202f 100644
--- a/eng/common/templates-official/job/source-index-stage1.yml
+++ b/eng/common/templates-official/job/source-index-stage1.yml
@@ -1,6 +1,7 @@
parameters:
runAsPublic: false
- sourceIndexPackageVersion: 1.0.1-20230228.2
+ sourceIndexUploadPackageVersion: 2.0.0-20240502.12
+ sourceIndexProcessBinlogPackageVersion: 1.0.1-20240129.2
sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json
sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci"
preSteps: []
@@ -14,15 +15,15 @@ jobs:
dependsOn: ${{ parameters.dependsOn }}
condition: ${{ parameters.condition }}
variables:
- - name: SourceIndexPackageVersion
- value: ${{ parameters.sourceIndexPackageVersion }}
+ - name: SourceIndexUploadPackageVersion
+ value: ${{ parameters.sourceIndexUploadPackageVersion }}
+ - name: SourceIndexProcessBinlogPackageVersion
+ value: ${{ parameters.sourceIndexProcessBinlogPackageVersion }}
- name: SourceIndexPackageSource
value: ${{ parameters.sourceIndexPackageSource }}
- name: BinlogPath
value: ${{ parameters.binlogPath }}
- - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- - group: source-dot-net stage1 variables
- - template: /eng/common/templates-official/variables/pool-providers.yml
+ - template: /eng/common/templates/variables/pool-providers.yml
${{ if ne(parameters.pool, '') }}:
pool: ${{ parameters.pool }}
@@ -33,24 +34,23 @@ jobs:
demands: ImageOverride -equals windows.vs2019.amd64.open
${{ if eq(variables['System.TeamProject'], 'internal') }}:
name: $(DncEngInternalBuildPool)
- image: windows.vs2022.amd64
- os: windows
+ demands: ImageOverride -equals windows.vs2019.amd64
steps:
- ${{ each preStep in parameters.preSteps }}:
- ${{ preStep }}
- task: UseDotNet@2
- displayName: Use .NET Core SDK 6
+ displayName: Use .NET 8 SDK
inputs:
packageType: sdk
- version: 6.0.x
+ version: 8.0.x
installationPath: $(Agent.TempDirectory)/dotnet
workingDirectory: $(Agent.TempDirectory)
- script: |
- $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools
- $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools
+ $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(sourceIndexProcessBinlogPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools
+ $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(sourceIndexUploadPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools
displayName: Download Tools
# Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk.
workingDirectory: $(Agent.TempDirectory)
@@ -62,7 +62,24 @@ jobs:
displayName: Process Binlog into indexable sln
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name)
- displayName: Upload stage1 artifacts to source index
- env:
- BLOB_CONTAINER_URL: $(source-dot-net-stage1-blob-container-url)
+ - task: AzureCLI@2
+ displayName: Get stage 1 auth token
+ inputs:
+ azureSubscription: 'SourceDotNet Stage1 Publish'
+ addSpnToEnvironment: true
+ scriptType: 'ps'
+ scriptLocation: 'inlineScript'
+ inlineScript: |
+ echo "##vso[task.setvariable variable=ARM_CLIENT_ID]$env:servicePrincipalId"
+ echo "##vso[task.setvariable variable=ARM_ID_TOKEN]$env:idToken"
+ echo "##vso[task.setvariable variable=ARM_TENANT_ID]$env:tenantId"
+
+ - script: |
+ echo "Client ID: $(ARM_CLIENT_ID)"
+ echo "ID Token: $(ARM_ID_TOKEN)"
+ echo "Tenant ID: $(ARM_TENANT_ID)"
+ az login --service-principal -u $(ARM_CLIENT_ID) --tenant $(ARM_TENANT_ID) --allow-no-subscriptions --federated-token $(ARM_ID_TOKEN)
+ displayName: "Login to Azure"
+
+ - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) -s netsourceindexstage1 -b stage1
+ displayName: Upload stage1 artifacts to source index
\ No newline at end of file
diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml
index b98202aa02..43ee0c202f 100644
--- a/eng/common/templates/job/source-index-stage1.yml
+++ b/eng/common/templates/job/source-index-stage1.yml
@@ -1,6 +1,7 @@
parameters:
runAsPublic: false
- sourceIndexPackageVersion: 1.0.1-20230228.2
+ sourceIndexUploadPackageVersion: 2.0.0-20240502.12
+ sourceIndexProcessBinlogPackageVersion: 1.0.1-20240129.2
sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json
sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci"
preSteps: []
@@ -14,14 +15,14 @@ jobs:
dependsOn: ${{ parameters.dependsOn }}
condition: ${{ parameters.condition }}
variables:
- - name: SourceIndexPackageVersion
- value: ${{ parameters.sourceIndexPackageVersion }}
+ - name: SourceIndexUploadPackageVersion
+ value: ${{ parameters.sourceIndexUploadPackageVersion }}
+ - name: SourceIndexProcessBinlogPackageVersion
+ value: ${{ parameters.sourceIndexProcessBinlogPackageVersion }}
- name: SourceIndexPackageSource
value: ${{ parameters.sourceIndexPackageSource }}
- name: BinlogPath
value: ${{ parameters.binlogPath }}
- - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- - group: source-dot-net stage1 variables
- template: /eng/common/templates/variables/pool-providers.yml
${{ if ne(parameters.pool, '') }}:
@@ -40,16 +41,16 @@ jobs:
- ${{ preStep }}
- task: UseDotNet@2
- displayName: Use .NET Core SDK 6
+ displayName: Use .NET 8 SDK
inputs:
packageType: sdk
- version: 6.0.x
+ version: 8.0.x
installationPath: $(Agent.TempDirectory)/dotnet
workingDirectory: $(Agent.TempDirectory)
- script: |
- $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools
- $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools
+ $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(sourceIndexProcessBinlogPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools
+ $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(sourceIndexUploadPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools
displayName: Download Tools
# Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk.
workingDirectory: $(Agent.TempDirectory)
@@ -61,7 +62,24 @@ jobs:
displayName: Process Binlog into indexable sln
- ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}:
- - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name)
- displayName: Upload stage1 artifacts to source index
- env:
- BLOB_CONTAINER_URL: $(source-dot-net-stage1-blob-container-url)
+ - task: AzureCLI@2
+ displayName: Get stage 1 auth token
+ inputs:
+ azureSubscription: 'SourceDotNet Stage1 Publish'
+ addSpnToEnvironment: true
+ scriptType: 'ps'
+ scriptLocation: 'inlineScript'
+ inlineScript: |
+ echo "##vso[task.setvariable variable=ARM_CLIENT_ID]$env:servicePrincipalId"
+ echo "##vso[task.setvariable variable=ARM_ID_TOKEN]$env:idToken"
+ echo "##vso[task.setvariable variable=ARM_TENANT_ID]$env:tenantId"
+
+ - script: |
+ echo "Client ID: $(ARM_CLIENT_ID)"
+ echo "ID Token: $(ARM_ID_TOKEN)"
+ echo "Tenant ID: $(ARM_TENANT_ID)"
+ az login --service-principal -u $(ARM_CLIENT_ID) --tenant $(ARM_TENANT_ID) --allow-no-subscriptions --federated-token $(ARM_ID_TOKEN)
+ displayName: "Login to Azure"
+
+ - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) -s netsourceindexstage1 -b stage1
+ displayName: Upload stage1 artifacts to source index
\ No newline at end of file
diff --git a/eng/packages/General.props b/eng/packages/General.props
index c67317652c..c02cfb54dd 100644
--- a/eng/packages/General.props
+++ b/eng/packages/General.props
@@ -51,7 +51,6 @@
-
diff --git a/global.json b/global.json
index f6d5259566..7c55ca32aa 100644
--- a/global.json
+++ b/global.json
@@ -16,7 +16,7 @@
"msbuild-sdks": {
"Microsoft.Build.NoTargets": "3.7.0",
"Microsoft.Build.Traversal": "3.2.0",
- "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24225.1",
- "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24225.1"
+ "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.24266.3",
+ "Microsoft.DotNet.Helix.Sdk": "8.0.0-beta.24266.3"
}
}
diff --git a/src/Libraries/Microsoft.Extensions.Telemetry/Microsoft.Extensions.Telemetry.csproj b/src/Libraries/Microsoft.Extensions.Telemetry/Microsoft.Extensions.Telemetry.csproj
index 053db6d3e6..9ad04ae889 100644
--- a/src/Libraries/Microsoft.Extensions.Telemetry/Microsoft.Extensions.Telemetry.csproj
+++ b/src/Libraries/Microsoft.Extensions.Telemetry/Microsoft.Extensions.Telemetry.csproj
@@ -37,7 +37,6 @@
-
diff --git a/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/README.md b/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/README.md
index 7c334ad800..c1dfddbb9f 100644
--- a/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/README.md
+++ b/src/Libraries/Microsoft.Extensions.TimeProvider.Testing/README.md
@@ -42,6 +42,102 @@ timeProvider.Advance(TimeSpan.FromSeconds(5));
myComponent.CheckState();
```
+## Use ConfigureAwait(true) with FakeTimeProvider.Advance
+
+The Advance method is used to simulate the passage of time. This can be useful in tests where you need to control the timing of asynchronous operations.
+When awaiting a task in a test that uses `FakeTimeProvider`, it's important to use `ConfigureAwait(true)`.
+
+Here's an example:
+
+```cs
+await provider.Delay(TimeSpan.FromSeconds(delay)).ConfigureAwait(true);
+```
+
+This ensures that the continuation of the awaited task (i.e., the code that comes after the await statement) runs in the original context.
+
+For a more realistic example, consider the following test using Polly:
+
+```cs
+using Polly;
+using Polly.Retry;
+
+public class SomeService(TimeProvider timeProvider)
+{
+ // Don't do this in real life, not thread safe
+ public int Tries { get; private set; }
+
+ private readonly ResiliencePipeline _retryPipeline = new ResiliencePipelineBuilder { TimeProvider = timeProvider }
+ .AddRetry(
+ new RetryStrategyOptions
+ {
+ ShouldHandle = new PredicateBuilder().Handle(),
+ Delay = TimeSpan.FromSeconds(1),
+ MaxRetryAttempts = 2,
+ BackoffType = DelayBackoffType.Linear,
+ })
+ .Build();
+
+ public async Task PollyRetry(double taskDelay, double cancellationSeconds)
+ {
+ CancellationTokenSource cts = new(TimeSpan.FromSeconds(cancellationSeconds), timeProvider);
+ Tries = 0;
+
+ // get a context from the pool and return it when done
+ var context = ResilienceContextPool.Shared.Get(
+ // ensure execution continues on captured context
+ continueOnCapturedContext: true,
+ cancellationToken: cts.Token);
+
+ var result = await _retryPipeline.ExecuteAsync(
+ async _ =>
+ {
+ Tries++;
+
+ // Simulate a task that takes some time to complete
+ await Task.Delay(TimeSpan.FromSeconds(taskDelay), timeProvider).ConfigureAwait(true);
+
+ if (Tries <= 2)
+ {
+ throw new InvalidOperationException();
+ }
+
+ return Tries;
+ },
+ context);
+
+ ResilienceContextPool.Shared.Return(context);
+
+ return result;
+ }
+}
+
+using Microsoft.Extensions.Time.Testing;
+
+public class SomeServiceTests
+{
+ [Fact]
+ public void PollyRetry_ShouldHave2Tries()
+ {
+ var timeProvider = new FakeTimeProvider();
+ var someService = new SomeService(timeProvider);
+
+ // Act
+ var result = someService.PollyRetry(taskDelay: 1, cancellationSeconds: 6);
+
+ // Advancing the time more than one second should resolves the first execution delay.
+ timeProvider.Advance(TimeSpan.FromMilliseconds(1001));
+
+ // Advancing the time more than the retry delay time of 1s,
+ // and less then the task execution delay should start the second try
+ timeProvider.Advance(TimeSpan.FromMilliseconds(1050));
+
+ // Assert
+ result.IsCompleted.Should().BeFalse();
+ someService.Tries.Should().Be(2);
+ }
+}
+```
+
## Feedback & Contributing
We welcome feedback and contributions in [our GitHub repo](https://github.com/dotnet/extensions).
diff --git a/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/FakeTimeProviderTests.cs b/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/FakeTimeProviderTests.cs
index b7c70a715a..0e438cfe4d 100644
--- a/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/FakeTimeProviderTests.cs
+++ b/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/FakeTimeProviderTests.cs
@@ -381,4 +381,57 @@ public class FakeTimeProviderTests
// Assert
Assert.Single(calls);
}
+
+ [Fact]
+ public void SimulateRetryPolicy()
+ {
+ // Arrange
+ var retries = 42;
+ var tries = 0;
+ var taskDelay = 0.5;
+ var delay = 1;
+ var provider = new FakeTimeProvider();
+
+ async Task simulatedPollyRetry()
+ {
+ while (true)
+ {
+ try
+ {
+ // simulate task that takes some time to complete
+ await provider.Delay(TimeSpan.FromSeconds(taskDelay));
+ tries++;
+
+ if (tries <= retries)
+ {
+ // the task failed, trigger retry
+ throw new InvalidOperationException();
+ }
+
+ return tries;
+ }
+ catch (InvalidOperationException)
+ {
+ // ConfigureAwait(true) is required to ensure that tasks continue on the captured context
+ await provider.Delay(TimeSpan.FromSeconds(delay)).ConfigureAwait(true);
+ }
+ }
+ }
+
+ // Act
+ var result = simulatedPollyRetry();
+
+ for (int i = 0; i < retries; i++)
+ {
+ // advance time for simulated task delay
+ provider.Advance(TimeSpan.FromSeconds(taskDelay));
+
+ // advance time for retry delay
+ provider.Advance(TimeSpan.FromSeconds(delay));
+ }
+
+ // Assert
+ Assert.False(result.IsCompleted);
+ Assert.Equal(retries, tries);
+ }
}
diff --git a/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/Microsoft.Extensions.TimeProvider.Testing.Tests.csproj b/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/Microsoft.Extensions.TimeProvider.Testing.Tests.csproj
index d0884c2ac5..142b573ae0 100644
--- a/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/Microsoft.Extensions.TimeProvider.Testing.Tests.csproj
+++ b/test/Libraries/Microsoft.Extensions.TimeProvider.Testing.Tests/Microsoft.Extensions.TimeProvider.Testing.Tests.csproj
@@ -5,7 +5,7 @@
-
+