Skip to content
Merged
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
fix(pipeline): timeout + retry InstallPlaywrightModule so a hung down…
…load fails fast

`npx playwright install --with-deps` ran with no per-attempt timeout
and no retry. When the network stalled mid-download (~500 MB of
browser binaries), the module sat at the outer 30-minute module
budget before failing, cascading into RunPlaywrightTestsModule,
UploadToNuGetModule, and CreateReleaseModule (run 25238011861).

Adds a 10-minute per-attempt timeout via a linked CancellationTokenSource
plus the standard ModuleConfiguration.WithRetryCount(2). Happy path
(~2 min on a warm runner) is unaffected; a hang now fails at 10 min
and retries instead of burning the full 30.
  • Loading branch information
thomhurst committed May 11, 2026
commit 8a53575a4200fc12b63630920d1b8db07235e5d2
21 changes: 18 additions & 3 deletions TUnit.Pipeline/Modules/InstallPlaywrightModule.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using ModularPipelines.Configuration;
using ModularPipelines.Context;
using ModularPipelines.Models;
using ModularPipelines.Modules;
Expand All @@ -7,8 +8,22 @@ namespace TUnit.Pipeline.Modules;

public class InstallPlaywrightModule : Module<CommandResult>
{
protected override async Task<CommandResult?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken) =>
await context.Shell.Bash.Command(
// Browser download is ~500 MB. 10 minutes is far past the happy-path
// (~2 min on a warm runner) but short enough that a hung connection gets
// killed and retried instead of burning the module's outer 30-min budget.
private static readonly TimeSpan PerAttemptTimeout = TimeSpan.FromMinutes(10);

protected override ModuleConfiguration Configure() => ModuleConfiguration.Create()
.WithRetryCount(2)
.Build();

protected override async Task<CommandResult?> ExecuteAsync(IModuleContext context, CancellationToken cancellationToken)
{
using var attemptCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
attemptCts.CancelAfter(PerAttemptTimeout);

return await context.Shell.Bash.Command(
new BashCommandOptions("npx playwright install --with-deps"),
cancellationToken);
attemptCts.Token);
}
}
Loading