Skip to content

Commit 47d01c7

Browse files
authored
Retry bundle generation a couple times to hopefully reduce CI instability (#50643)
1 parent f0a1b88 commit 47d01c7

File tree

1 file changed

+51
-11
lines changed

1 file changed

+51
-11
lines changed

src/Tasks/Microsoft.NET.Build.Tasks/GenerateBundle.cs

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,56 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
#nullable disable
5-
64
using Microsoft.Build.Framework;
75
using Microsoft.NET.HostModel.Bundle;
86

97
namespace Microsoft.NET.Build.Tasks
108
{
11-
public class GenerateBundle : TaskBase
9+
public class GenerateBundle : TaskBase, ICancelableTask
1210
{
11+
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
12+
private readonly Random _jitter =
13+
#if NET
14+
Random.Shared;
15+
#else
16+
new Random();
17+
#endif
18+
1319
[Required]
14-
public ITaskItem[] FilesToBundle { get; set; }
20+
public ITaskItem[] FilesToBundle { get; set; } = null!;
1521
[Required]
16-
public string AppHostName { get; set; }
22+
public string AppHostName { get; set; } = null!;
1723
[Required]
1824
public bool IncludeSymbols { get; set; }
1925
[Required]
2026
public bool IncludeNativeLibraries { get; set; }
2127
[Required]
2228
public bool IncludeAllContent { get; set; }
2329
[Required]
24-
public string TargetFrameworkVersion { get; set; }
30+
public string TargetFrameworkVersion { get; set; } = null!;
2531
[Required]
26-
public string RuntimeIdentifier { get; set; }
32+
public string RuntimeIdentifier { get; set; } = null!;
2733
[Required]
28-
public string OutputDir { get; set; }
34+
public string OutputDir { get; set; } = null!;
2935
[Required]
3036
public bool ShowDiagnosticOutput { get; set; }
3137
[Required]
3238
public bool EnableCompressionInSingleFile { get; set; }
3339
public bool EnableMacOsCodeSign { get; set; } = true;
3440

3541
[Output]
36-
public ITaskItem[] ExcludedFiles { get; set; }
42+
public ITaskItem[] ExcludedFiles { get; set; } = null!;
43+
44+
public int? RetryCount { get; set; } = 3;
45+
46+
public void Cancel() => _cancellationTokenSource.Cancel();
3747

3848
protected override void ExecuteCore()
49+
{
50+
ExecuteWithRetry().GetAwaiter().GetResult();
51+
}
52+
53+
private async Task ExecuteWithRetry()
3954
{
4055
OSPlatform targetOS = RuntimeIdentifier.StartsWith("win") ? OSPlatform.Windows :
4156
RuntimeIdentifier.StartsWith("osx") ? OSPlatform.OSX :
@@ -78,15 +93,40 @@ protected override void ExecuteCore()
7893
bundleRelativePath: item.GetMetadata(MetadataKeys.RelativePath)));
7994
}
8095

81-
bundler.GenerateBundle(fileSpec);
96+
// GenerateBundle has been throwing IOException intermittently in CI runs when accessing the singlefilehost binary specifically.
97+
// We hope that it's a Defender issue and that a quick retry will paper over the intermittent delay.
98+
await DoWithRetry(() => bundler.GenerateBundle(fileSpec));
8299

83100
// Certain files are excluded from the bundle, based on BundleOptions.
84101
// For example:
85102
// Native files and contents files are excluded by default.
86103
// hostfxr and hostpolicy are excluded until singlefilehost is available.
87104
// Return the set of excluded files in ExcludedFiles, so that they can be placed in the publish directory.
88105

89-
ExcludedFiles = FilesToBundle.Zip(fileSpec, (item, spec) => (spec.Excluded) ? item : null).Where(x => x != null).ToArray();
106+
ExcludedFiles = FilesToBundle.Zip(fileSpec, (item, spec) => (spec.Excluded) ? item : null).Where(x => x != null).ToArray()!;
107+
}
108+
109+
public async Task DoWithRetry(Action action)
110+
{
111+
bool triedOnce = false;
112+
while (RetryCount > 0 || !triedOnce)
113+
{
114+
if (_cancellationTokenSource.IsCancellationRequested)
115+
{
116+
break;
117+
}
118+
try
119+
{
120+
action();
121+
break;
122+
}
123+
catch (IOException) when (RetryCount > 0)
124+
{
125+
Log.LogMessage(MessageImportance.High, $"Unable to access file during bundling. Retrying {RetryCount} more times...");
126+
RetryCount--;
127+
await Task.Delay(_jitter.Next(10, 50));
128+
}
129+
}
90130
}
91131
}
92132
}

0 commit comments

Comments
 (0)