Skip to content
Open
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion patches/last_processed_commit.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
c814d7ca46f403a860f84c4e54b78c5a2d17e045
4c6d85cfc08ee95c04bf0d7f7756eede55ac5713
4 changes: 2 additions & 2 deletions patches/runner-main-sdk8-ppc64le.patch
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ index 9c069b12..d26b0dc2 100644
<!-- Set TRACE/DEBUG vars -->
<PropertyGroup>
diff --git a/src/Misc/externals.sh b/src/Misc/externals.sh
index 46384bb4..fc2a9456 100755
index 4947a9bd..8103a2db 100755
--- a/src/Misc/externals.sh
+++ b/src/Misc/externals.sh
@@ -187,3 +187,13 @@ fi
Expand Down Expand Up @@ -284,4 +284,4 @@ index 056a312e..3f9a3679 100644
<Exec Command="%22$(DesktopMSBuild)%22 Runner.Service/Windows/RunnerService.csproj /p:Configuration=$(BUILDCONFIG) /p:PackageRuntime=$(PackageRuntime) /p:OutputPath=%22$(MSBuildProjectDirectory)/../_layout/bin%22" ConsoleToMSBuild="true" Condition="'$(PackageRuntime)' == 'win-x64' Or '$(PackageRuntime)' == 'win-x86' Or '$(PackageRuntime)' == 'win-arm64'" />
</Target>

# From upstream commit: c814d7ca46f403a860f84c4e54b78c5a2d17e045
# From upstream commit: 4c6d85cfc08ee95c04bf0d7f7756eede55ac5713
4 changes: 2 additions & 2 deletions patches/runner-main-sdk8-s390x.patch
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ index 9c069b12..d26b0dc2 100644
<!-- Set TRACE/DEBUG vars -->
<PropertyGroup>
diff --git a/src/Misc/externals.sh b/src/Misc/externals.sh
index 46384bb4..fc2a9456 100755
index 4947a9bd..8103a2db 100755
--- a/src/Misc/externals.sh
+++ b/src/Misc/externals.sh
@@ -187,3 +187,13 @@ fi
Expand Down Expand Up @@ -284,4 +284,4 @@ index 056a312e..3f9a3679 100644
<Exec Command="%22$(DesktopMSBuild)%22 Runner.Service/Windows/RunnerService.csproj /p:Configuration=$(BUILDCONFIG) /p:PackageRuntime=$(PackageRuntime) /p:OutputPath=%22$(MSBuildProjectDirectory)/../_layout/bin%22" ConsoleToMSBuild="true" Condition="'$(PackageRuntime)' == 'win-x64' Or '$(PackageRuntime)' == 'win-x86' Or '$(PackageRuntime)' == 'win-arm64'" />
</Target>

# From upstream commit: c814d7ca46f403a860f84c4e54b78c5a2d17e045
# From upstream commit: 4c6d85cfc08ee95c04bf0d7f7756eede55ac5713
2 changes: 1 addition & 1 deletion src/Misc/externals.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ NODE_ALPINE_URL=https://github.com/actions/alpine_nodejs/releases/download
# When you update Node versions you must also create a new release of alpine_nodejs at that updated version.
# Follow the instructions here: https://github.com/actions/alpine_nodejs?tab=readme-ov-file#getting-started
NODE20_VERSION="20.20.2"
NODE24_VERSION="24.17.0"
NODE24_VERSION="24.18.0"

get_abs_path() {
# exploits the fact that pwd will print abs path when no args
Expand Down
109 changes: 103 additions & 6 deletions src/Runner.Worker/ActionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,30 @@ public sealed class ActionManager : RunnerService, IActionManager
{
throw new Exception($"Missing download info for {lookupKey}");
}
await DownloadRepositoryActionAsync(executionContext, downloadInfo);

Exception downloadFailure = null;
try
{
await DownloadRepositoryActionAsync(executionContext, downloadInfo);
}
catch (Exception ex)
{
// record the exception for telemetry, and rethrow the original exception to fail the step.
downloadFailure = ex;
throw;
}
finally
{
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
{
Type = JobTelemetryType.General,
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
{
Operation = "download_action",
Result = downloadFailure == null ? "succeeded" : downloadFailure.GetType().Name
}, Newtonsoft.Json.Formatting.None)}"
});
}
}

// Parse action.yml and collect composite sub-actions for batched
Expand Down Expand Up @@ -398,7 +421,30 @@ public sealed class ActionManager : RunnerService, IActionManager
if (repositoryActions.Count > 0)
{
// Get the download info
var downloadInfos = await GetDownloadInfoAsync(executionContext, repositoryActions);
IDictionary<string, WebApi.ActionDownloadInfo> downloadInfos = null;
Exception resolveFailure = null;
try
{
downloadInfos = await GetDownloadInfoAsync(executionContext, repositoryActions);
}
catch (Exception ex)
{
// record the exception for telemetry, and rethrow the original exception to fail the step.
resolveFailure = ex;
throw;
}
finally
{
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
{
Type = JobTelemetryType.General,
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
{
Operation = "resolve_actions",
Result = resolveFailure == null ? "succeeded" : resolveFailure.GetType().Name
}, Newtonsoft.Json.Formatting.None)}"
});
}

// Download each action
foreach (var action in repositoryActions)
Expand All @@ -414,7 +460,29 @@ public sealed class ActionManager : RunnerService, IActionManager
throw new Exception($"Missing download info for {lookupKey}");
}

await DownloadRepositoryActionAsync(executionContext, downloadInfo);
Exception downloadFailure = null;
try
{
await DownloadRepositoryActionAsync(executionContext, downloadInfo);
}
catch (Exception ex)
{
// record the exception for telemetry, and rethrow the original exception to fail the step.
downloadFailure = ex;
throw;
}
finally
{
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
{
Type = JobTelemetryType.General,
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
{
Operation = "download_action",
Result = downloadFailure == null ? "succeeded" : downloadFailure.GetType().Name
}, Newtonsoft.Json.Formatting.None)}"
});
}
}

// More preparation based on content in the repository (action.yml)
Expand Down Expand Up @@ -980,10 +1048,33 @@ private async Task ResolveNewActionsAsync(IExecutionContext executionContext, Li

if (actionsToResolve.Count > 0)
{
var downloadInfos = await GetDownloadInfoAsync(executionContext, actionsToResolve);
foreach (var kvp in downloadInfos)
IDictionary<string, WebApi.ActionDownloadInfo> downloadInfos = null;
Exception resolveFailure = null;
try
{
resolvedDownloadInfos[kvp.Key] = kvp.Value;
downloadInfos = await GetDownloadInfoAsync(executionContext, actionsToResolve);
foreach (var kvp in downloadInfos)
{
resolvedDownloadInfos[kvp.Key] = kvp.Value;
}
}
catch (Exception ex)
{
// record the exception for telemetry, and rethrow the original exception to fail the step.
resolveFailure = ex;
throw;
}
finally
{
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
{
Type = JobTelemetryType.General,
Message = $"resolve_download_actions_telemetry:{StringUtil.ConvertToJson(new ActionTelemetryPayload
{
Operation = "resolve_actions",
Result = resolveFailure == null ? "succeeded" : resolveFailure.GetType().Name
}, Newtonsoft.Json.Formatting.None)}"
});
}
}
}
Expand Down Expand Up @@ -1209,6 +1300,12 @@ private async Task DownloadRepositoryActionAsync(IExecutionContext executionCont

private string GetWatermarkFilePath(string directory) => directory + ".completed";

private sealed class ActionTelemetryPayload
{
public string Operation { get; set; }
public string Result { get; set; }
}

private ActionSetupInfo PrepareRepositoryActionAsync(IExecutionContext executionContext, Pipelines.ActionStep repositoryAction)
{
var repositoryReference = repositoryAction.Reference as Pipelines.RepositoryPathReference;
Expand Down
65 changes: 65 additions & 0 deletions src/Test/L0/Worker/ActionManagerL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ public async void PrepareActions_DownloadActionFromDotCom_OnPremises_Legacy()

var actionYamlFile = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), ActionName, "main", "action.yml");
Assert.True(File.Exists(actionYamlFile));

var telemetryMessages = GetTelemetryMessages();
Assert.True(ContainsTelemetry(telemetryMessages, "resolve_actions"));
Assert.True(ContainsTelemetry(telemetryMessages, "succeeded"));
Assert.True(ContainsTelemetry(telemetryMessages, "download_action"));
_hc.GetTrace().Info(File.ReadAllText(actionYamlFile));
}
finally
Expand Down Expand Up @@ -148,6 +153,11 @@ public async void PrepareActions_DownloadActionFromDotCom_ZipFileError()

// Act + Assert
await Assert.ThrowsAsync<InvalidActionArchiveException>(async () => await _actionManager.PrepareActionsAsync(_ec.Object, actions));

var telemetryMessages = GetTelemetryMessages();
Assert.True(ContainsTelemetry(telemetryMessages, "resolve_actions"));
Assert.True(ContainsTelemetry(telemetryMessages, "download_action"));
Assert.True(ContainsTelemetry(telemetryMessages, "InvalidActionArchiveException"));
}
finally
{
Expand Down Expand Up @@ -215,6 +225,51 @@ public async void PrepareActions_DownloadUnknownActionFromGraph_OnPremises_Legac
}
}

[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task PrepareActions_ResolveActionDownloadInfo_RecordsTelemetry_OnFailure()
{
try
{
// Arrange
Setup();
_ec.Object.Global.Variables.Set(Constants.Variables.System.JobRequestType, "RunnerJobRequest");

_launchServer
.Setup(x => x.ResolveActionsDownloadInfoAsync(It.IsAny<Guid>(), It.IsAny<Guid>(), It.IsAny<ActionReferenceList>(), It.IsAny<CancellationToken>(), It.IsAny<bool>()))
.ThrowsAsync(new Exception("resolve failed"));

var actions = new List<Pipelines.JobStep>
{
new Pipelines.ActionStep()
{
Name = "action",
Id = Guid.NewGuid(),
Reference = new Pipelines.RepositoryPathReference()
{
Name = "actions/checkout",
Ref = "v4",
RepositoryType = "GitHub"
}
}
};

// Act + Assert
await Assert.ThrowsAsync<FailedToResolveActionDownloadInfoException>(async () => await _actionManager.PrepareActionsAsync(_ec.Object, actions));

var telemetryMessages = GetTelemetryMessages();
Assert.Equal(1, telemetryMessages.Count(message =>
message.Contains("resolve_actions", StringComparison.OrdinalIgnoreCase)
&& !message.Contains("\"result\":\"succeeded\"", StringComparison.OrdinalIgnoreCase)));
Assert.False(ContainsTelemetry(telemetryMessages, "resolve_actions\",\"result\":\"succeeded"));
}
finally
{
Teardown();
}
}

#if OS_LINUX
[Fact]
[Trait("Level", "L0")]
Expand Down Expand Up @@ -3333,6 +3388,16 @@ private void Teardown()
}
}

private IList<string> GetTelemetryMessages()
{
return _ec.Object.Global.JobTelemetry.Select(x => x.Message).ToList();
}

private static bool ContainsTelemetry(IList<string> telemetryMessages, string expectedFragment)
{
return telemetryMessages.Any(message => message.Contains(expectedFragment, StringComparison.OrdinalIgnoreCase));
}

[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
Expand Down
Loading