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
9 changes: 9 additions & 0 deletions src/Runner.Common/JobServerQueue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -837,6 +837,15 @@ private List<TimelineRecord> MergeTimelineRecords(List<TimelineRecord> timelineR
timelineRecord.Variables[variable.Key] = variable.Value.Clone();
}
}

// Merge background step metadata
if (rec.IsBackground)
{
timelineRecord.IsBackground = rec.IsBackground;
}
timelineRecord.BackgroundControlType = rec.BackgroundControlType ?? timelineRecord.BackgroundControlType;
timelineRecord.BackgroundControlStepIds = rec.BackgroundControlStepIds ?? timelineRecord.BackgroundControlStepIds;
timelineRecord.ParallelGroupId = rec.ParallelGroupId ?? timelineRecord.ParallelGroupId;
}
else
{
Expand Down
15 changes: 12 additions & 3 deletions src/Runner.Worker/ActionCommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ private void ValidateStopToken(IExecutionContext context, string stopToken)
Message = $"Invoked ::stopCommand:: with token: [{stopToken}]",
Type = JobTelemetryType.ActionCommand
};
context.Global.JobTelemetry.Add(telemetry);
lock (context.Global.CollectionLock)
{
context.Global.JobTelemetry.Add(telemetry);
}
}

if (isTokenInvalid && !allowUnsecureStopCommandTokens)
Expand Down Expand Up @@ -326,7 +329,10 @@ public void ProcessCommand(IExecutionContext context, string line, ActionCommand
Type = JobTelemetryType.ActionCommand,
Message = "DeprecatedCommand: set-output"
};
context.Global.JobTelemetry.Add(telemetry);
lock (context.Global.CollectionLock)
{
context.Global.JobTelemetry.Add(telemetry);
}
}

if (!command.Properties.TryGetValue(SetOutputCommandProperties.Name, out string outputName) || string.IsNullOrEmpty(outputName))
Expand Down Expand Up @@ -372,7 +378,10 @@ public void ProcessCommand(IExecutionContext context, string line, ActionCommand
Type = JobTelemetryType.ActionCommand,
Message = "DeprecatedCommand: save-state"
};
context.Global.JobTelemetry.Add(telemetry);
lock (context.Global.CollectionLock)
{
context.Global.JobTelemetry.Add(telemetry);
}
}

if (!command.Properties.TryGetValue(SaveStateCommandProperties.Name, out string stateName) || string.IsNullOrEmpty(stateName))
Expand Down
22 changes: 14 additions & 8 deletions src/Runner.Worker/ActionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1068,11 +1068,14 @@ private async Task DownloadRepositoryActionAsync(IExecutionContext executionCont
}

executionContext.Debug($"Created symlink from cached directory '{cacheDirectory}' to '{destDirectory}'");
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
lock (executionContext.Global.CollectionLock)
{
Type = JobTelemetryType.General,
Message = $"Action archive cache usage: {downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha} use cache {useActionArchiveCache} has cache {hasActionArchiveCache} via symlink"
});
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
{
Type = JobTelemetryType.General,
Message = $"Action archive cache usage: {downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha} use cache {useActionArchiveCache} has cache {hasActionArchiveCache} via symlink"
});
}

Trace.Info("Finished getting action repository.");
return;
Expand Down Expand Up @@ -1108,11 +1111,14 @@ private async Task DownloadRepositoryActionAsync(IExecutionContext executionCont
}
}

executionContext.Global.JobTelemetry.Add(new JobTelemetry()
lock (executionContext.Global.CollectionLock)
{
Type = JobTelemetryType.General,
Message = $"Action archive cache usage: {downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha} use cache {useActionArchiveCache} has cache {hasActionArchiveCache}"
});
executionContext.Global.JobTelemetry.Add(new JobTelemetry()
{
Type = JobTelemetryType.General,
Message = $"Action archive cache usage: {downloadInfo.ResolvedNameWithOwner}@{downloadInfo.ResolvedSha} use cache {useActionArchiveCache} has cache {hasActionArchiveCache}"
});
}

if (!useActionArchiveCache)
{
Expand Down
10 changes: 8 additions & 2 deletions src/Runner.Worker/ActionManifestManagerWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,10 @@ private void RecordMismatch(IExecutionContext context, string methodName)
{
context.Global.HasActionManifestMismatch = true;
var telemetry = new JobTelemetry { Type = JobTelemetryType.General, Message = $"ActionManifestMismatch: {methodName}" };
context.Global.JobTelemetry.Add(telemetry);
lock (context.Global.CollectionLock)
{
context.Global.JobTelemetry.Add(telemetry);
}
}
}

Expand All @@ -456,7 +459,10 @@ private void RecordComparisonError(IExecutionContext context, string errorDetail
{
context.Global.HasActionManifestMismatch = true;
var telemetry = new JobTelemetry { Type = JobTelemetryType.General, Message = $"ActionManifestComparisonError: {errorDetails}" };
context.Global.JobTelemetry.Add(telemetry);
lock (context.Global.CollectionLock)
{
context.Global.JobTelemetry.Add(telemetry);
}
}
}

Expand Down
95 changes: 91 additions & 4 deletions src/Runner.Worker/ExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ public interface IExecutionContext : IRunnerService
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, ActionRunStage stage, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null, bool isEmbedded = false, List<Issue> embeddedIssueCollector = null, CancellationTokenSource cancellationTokenSource = null, Guid embeddedId = default(Guid), string siblingScopeName = null, TimeSpan? timeout = null);
IExecutionContext CreateEmbeddedChild(string scopeName, string contextName, Guid embeddedId, ActionRunStage stage, Dictionary<string, string> intraActionState = null, string siblingScopeName = null);

// Background step deferral properties
Dictionary<string, string> DeferredOutputs { get; set; }
Dictionary<string, string> DeferredEnvironmentVariables { get; set; }
List<string> DeferredPrependPath { get; set; }
bool DeferOutcomeConclusion { get; set; }

// logging
long Write(string tag, string message);
void QueueAttachFile(string type, string name, string filePath);
Expand All @@ -100,11 +106,18 @@ public interface IExecutionContext : IRunnerService
void SetGitHubContext(string name, string value);
void SetOutput(string name, string value, out string reference);
void SetTimeout(TimeSpan? timeout);

// Background step deferral flush methods
void FlushDeferredOutputs();
void FlushDeferredEnvironment();
void FlushDeferredOutcomeConclusion();

void AddIssue(Issue issue, ExecutionContextLogOptions logOptions);
void Progress(int percentage, string currentOperation = null);
void UpdateDetailTimelineRecord(TimelineRecord record);

void UpdateTimelineRecordDisplayName(string displayName);
void SetBackgroundStepMetadata(bool isBackground = false, string backgroundControlType = null, string[] backgroundControlStepIds = null, string parallelGroupId = null);

// matchers
void Add(OnMatcherChanged handler);
Expand Down Expand Up @@ -279,6 +292,12 @@ public JobContext JobContext

public List<string> StepEnvironmentOverrides { get; } = new List<string>();

// Background step deferral properties
public Dictionary<string, string> DeferredOutputs { get; set; }
public Dictionary<string, string> DeferredEnvironmentVariables { get; set; }
public List<string> DeferredPrependPath { get; set; }
public bool DeferOutcomeConclusion { get; set; }

public override void Initialize(IHostContext hostContext)
{
base.Initialize(hostContext);
Expand Down Expand Up @@ -516,6 +535,11 @@ public TaskResult Complete(TaskResult? result = null, string currentOperation =
Annotations = new List<Annotation>()
};

// Populate background step metadata from timeline record fields
stepResult.IsBackground = _record.IsBackground;
stepResult.BackgroundControlType = _record.BackgroundControlType;
stepResult.BackgroundControlStepIds = _record.BackgroundControlStepIds;

_record.Issues?.ForEach(issue =>
{
var annotation = issue.ToAnnotation();
Expand All @@ -541,7 +565,10 @@ public TaskResult Complete(TaskResult? result = null, string currentOperation =
var annotation = issue.ToAnnotation();
if (annotation != null)
{
Global.JobAnnotations.Add(annotation.Value);
lock (Global.CollectionLock)
{
Global.JobAnnotations.Add(annotation.Value);
}
if (annotation.Value.IsInfrastructureIssue && string.IsNullOrEmpty(Global.InfrastructureFailureCategory))
{
Global.InfrastructureFailureCategory = issue.Category;
Expand All @@ -559,11 +586,22 @@ public TaskResult Complete(TaskResult? result = null, string currentOperation =

_logger.End();

UpdateGlobalStepsContext();
if (!DeferOutcomeConclusion)
{
UpdateGlobalStepsContext();
}

return Result.Value;
}

public void FlushDeferredOutcomeConclusion()
{
if (DeferOutcomeConclusion)
{
UpdateGlobalStepsContext();
}
}

public void UpdateGlobalStepsContext()
{
// Skip if generated context name. Generated context names start with "__". After 3.2 the server will never send an empty context name.
Expand Down Expand Up @@ -639,6 +677,40 @@ public void SetOutput(string name, string value, out string reference)
Global.StepsContext.SetOutput(ScopeName, ContextName, name, value, out reference);
}

public void FlushDeferredOutputs()
{
if (DeferredOutputs == null || DeferredOutputs.Count == 0)
{
return;
}

foreach (var kvp in DeferredOutputs)
{
Global.StepsContext.SetOutput(ScopeName, ContextName, kvp.Key, kvp.Value, out _);
}
}

public void FlushDeferredEnvironment()
{
if (DeferredEnvironmentVariables != null)
{
foreach (var kvp in DeferredEnvironmentVariables)
{
Global.EnvironmentVariables[kvp.Key] = kvp.Value;
SetEnvContext(kvp.Key, kvp.Value);
}
}

if (DeferredPrependPath != null)
{
foreach (var path in DeferredPrependPath)
{
Global.PrependPath.RemoveAll(x => string.Equals(x, path, StringComparison.CurrentCulture));
Global.PrependPath.Add(path);
}
}
}

public void SetTimeout(TimeSpan? timeout)
{
if (timeout != null)
Expand Down Expand Up @@ -812,6 +884,15 @@ public void UpdateTimelineRecordDisplayName(string displayName)
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
}

public void SetBackgroundStepMetadata(bool isBackground = false, string backgroundControlType = null, string[] backgroundControlStepIds = null, string parallelGroupId = null)
{
_record.IsBackground = isBackground;
_record.BackgroundControlType = backgroundControlType;
_record.BackgroundControlStepIds = backgroundControlStepIds;
_record.ParallelGroupId = parallelGroupId;
_jobServerQueue.QueueTimelineRecordUpdate(_mainTimelineId, _record);
}

public void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token)
{
// Validation
Expand Down Expand Up @@ -1196,7 +1277,10 @@ public void PublishStepTelemetry()
}

Trace.Info($"Publish step telemetry for current step {StringUtil.ConvertToJson(StepTelemetry)}.");
Global.StepsTelemetry.Add(StepTelemetry);
lock (Global.CollectionLock)
{
Global.StepsTelemetry.Add(StepTelemetry);
}
_stepTelemetryPublished = true;
}
}
Expand Down Expand Up @@ -1335,7 +1419,10 @@ public void ApplyContinueOnError(TemplateToken continueOnErrorToken)
Trace.Info($"Updated step result (continue on error)");
}

UpdateGlobalStepsContext();
if (!DeferOutcomeConclusion)
{
UpdateGlobalStepsContext();
}
}

internal IPipelineTemplateEvaluator ToPipelineTemplateEvaluatorInternal(bool allowServiceContainerCommand, ObjectTemplating.ITraceWriter traceWriter = null)
Expand Down
38 changes: 33 additions & 5 deletions src/Runner.Worker/FileCommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,18 @@ public void ProcessCommand(IExecutionContext context, string filePath, Container
{
continue;
}
context.Global.PrependPath.RemoveAll(x => string.Equals(x, line, StringComparison.CurrentCulture));
context.Global.PrependPath.Add(line);
if (context.DeferredPrependPath != null)
{
// Background step: buffer path additions until wait/wait-all
context.DeferredPrependPath.RemoveAll(x => string.Equals(x, line, StringComparison.CurrentCulture));
context.DeferredPrependPath.Add(line);
context.Debug($"Deferred prepend path '{line}' for background step");
}
else
{
context.Global.PrependPath.RemoveAll(x => string.Equals(x, line, StringComparison.CurrentCulture));
context.Global.PrependPath.Add(line);
}
}
}
}
Expand Down Expand Up @@ -172,8 +182,17 @@ private static void SetEnvironmentVariable(
string name,
string value)
{
context.Global.EnvironmentVariables[name] = value;
context.SetEnvContext(name, value);
if (context.DeferredEnvironmentVariables != null)
{
// Background step: buffer env changes until wait/wait-all
context.DeferredEnvironmentVariables[name] = value;
context.Debug($"Deferred env '{name}' for background step");
}
else
{
context.Global.EnvironmentVariables[name] = value;
context.SetEnvContext(name, value);
}
context.Debug($"{name}='{value}'");
}

Expand Down Expand Up @@ -302,7 +321,16 @@ public void ProcessCommand(IExecutionContext context, string filePath, Container
var pairs = new EnvFileKeyValuePairs(context, filePath);
foreach (var pair in pairs)
{
context.SetOutput(pair.Key, pair.Value, out var reference);
if (context.DeferredOutputs != null)
{
// Background step: buffer outputs until wait/wait-all
context.DeferredOutputs[pair.Key] = pair.Value;
context.Debug($"Deferred output '{pair.Key}' for background step");
}
else
{
context.SetOutput(pair.Key, pair.Value, out var reference);
}
context.Debug($"Set output {pair.Key} = {pair.Value}");
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/Runner.Worker/GlobalContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ namespace GitHub.Runner.Worker
{
public sealed class GlobalContext
{
// Lock for thread-safe access to shared collections during concurrent background step execution
public readonly object CollectionLock = new object();
Comment thread
lokesh755 marked this conversation as resolved.

public ContainerInfo Container { get; set; }
public List<ServiceEndpoint> Endpoints { get; set; }
public IDictionary<String, String> EnvironmentVariables { get; set; }
Expand Down
20 changes: 16 additions & 4 deletions src/Runner.Worker/Handlers/HandlerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,17 +115,26 @@ public IHandler Create(
if (string.Equals(finalNodeVersion, Constants.Runner.NodeMigration.Node24, StringComparison.OrdinalIgnoreCase))
{
// Action was upgraded from node20 to node24
executionContext.Global.UpgradedToNode24Actions?.Add(actionName);
lock (executionContext.Global.CollectionLock)
{
executionContext.Global.UpgradedToNode24Actions?.Add(actionName);
}
}
else if (ShouldTrackAsArm32Node20(deprecateArm32, nodeVersion, finalNodeVersion, platformWarningMessage))
{
// Action is on node20 because ARM32 can't run node24
executionContext.Global.Arm32Node20Actions?.Add(actionName);
lock (executionContext.Global.CollectionLock)
{
executionContext.Global.Arm32Node20Actions?.Add(actionName);
}
}
else if (warnOnNode20)
{
// Action is still running on node20 (general case)
executionContext.Global.DeprecatedNode20Actions?.Add(actionName);
lock (executionContext.Global.CollectionLock)
{
executionContext.Global.DeprecatedNode20Actions?.Add(actionName);
}
}
}

Expand Down Expand Up @@ -159,7 +168,10 @@ public IHandler Create(

if (!string.IsNullOrEmpty(actionName) && ShouldTrackAsArm32Node20(deprecateArm32, preferredVersion, finalNodeVersion, platformWarningMessage))
{
executionContext.Global.Arm32Node20Actions?.Add(actionName);
lock (executionContext.Global.CollectionLock)
{
executionContext.Global.Arm32Node20Actions?.Add(actionName);
}
}
}

Expand Down
Loading
Loading