From c6a124e18496a6e5d2357415052d1799afc64b63 Mon Sep 17 00:00:00 2001 From: Driele Neves Ribeiro Date: Thu, 28 May 2026 13:15:49 -0400 Subject: [PATCH] Populate telemetry for non-action post-job steps (#4463) Co-authored-by: Tingluo Huang --- src/Runner.Worker/ExecutionContext.cs | 5 + src/Test/L0/Worker/ExecutionContextL0.cs | 113 +++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/src/Runner.Worker/ExecutionContext.cs b/src/Runner.Worker/ExecutionContext.cs index d071790f3..b753c152b 100644 --- a/src/Runner.Worker/ExecutionContext.cs +++ b/src/Runner.Worker/ExecutionContext.cs @@ -337,6 +337,11 @@ namespace GitHub.Runner.Worker } step.ExecutionContext = Root.CreatePostChild(step.DisplayName, IntraActionState, siblingScopeName); + if (step is JobExtensionRunner) + { + step.ExecutionContext.StepTelemetry.Type = "runner"; + step.ExecutionContext.StepTelemetry.Action = step.DisplayName.ToLowerInvariant().Replace(' ', '_'); + } Root.PostJobSteps.Push(step); } diff --git a/src/Test/L0/Worker/ExecutionContextL0.cs b/src/Test/L0/Worker/ExecutionContextL0.cs index d35be3acc..553c8b154 100644 --- a/src/Test/L0/Worker/ExecutionContextL0.cs +++ b/src/Test/L0/Worker/ExecutionContextL0.cs @@ -361,6 +361,119 @@ namespace GitHub.Runner.Common.Tests.Worker } } + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public void RegisterPostJobStep_JobExtensionRunner_DefaultsRunnerTelemetry() + { + using (TestHostContext hc = CreateTestContext()) + { + // Arrange: Create a job request message. + TaskOrchestrationPlanReference plan = new(); + TimelineReference timeline = new(); + Guid jobId = Guid.NewGuid(); + string jobName = "some job name"; + var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary(), new List(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List(), null, null, null, null, null); + jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource() + { + Alias = Pipelines.PipelineConstants.SelfAlias, + Id = "github", + Version = "sha1" + }); + jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData(); + + var pagingLogger1 = new Mock(); + var pagingLogger2 = new Mock(); + var jobServerQueue = new Mock(); + jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny(), It.IsAny())); + + hc.EnqueueInstance(pagingLogger1.Object); + hc.EnqueueInstance(pagingLogger2.Object); + hc.SetSingleton(jobServerQueue.Object); + + var jobContext = new Runner.Worker.ExecutionContext(); + jobContext.Initialize(hc); + + // Act. + jobContext.InitializeJob(jobRequest, CancellationToken.None); + + var extensionStep = new JobExtensionRunner( + runAsync: (_, _) => System.Threading.Tasks.Task.CompletedTask, + condition: "always()", + displayName: "Create Custom Image", + data: null); + + jobContext.RegisterPostJobStep(extensionStep); + + // Assert: telemetry defaults are populated for non-action post-job steps. + Assert.NotNull(extensionStep.ExecutionContext); + Assert.Equal("runner", extensionStep.ExecutionContext.StepTelemetry.Type); + Assert.Equal("create_custom_image", extensionStep.ExecutionContext.StepTelemetry.Action); + } + } + + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public void RegisterPostJobStep_ActionRunner_DoesNotOverrideTelemetry() + { + using (TestHostContext hc = CreateTestContext()) + { + // Arrange: Create a job request message. + TaskOrchestrationPlanReference plan = new(); + TimelineReference timeline = new(); + Guid jobId = Guid.NewGuid(); + string jobName = "some job name"; + var jobRequest = new Pipelines.AgentJobRequestMessage(plan, timeline, jobId, jobName, jobName, null, null, null, new Dictionary(), new List(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List(), null, null, null, null, null); + jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource() + { + Alias = Pipelines.PipelineConstants.SelfAlias, + Id = "github", + Version = "sha1" + }); + jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData(); + + var pagingLogger1 = new Mock(); + var pagingLogger2 = new Mock(); + var pagingLogger3 = new Mock(); + var pagingLogger4 = new Mock(); + var jobServerQueue = new Mock(); + jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny(), It.IsAny())); + + var actionRunner = new ActionRunner(); + actionRunner.Initialize(hc); + + hc.EnqueueInstance(pagingLogger1.Object); + hc.EnqueueInstance(pagingLogger2.Object); + hc.EnqueueInstance(pagingLogger3.Object); + hc.EnqueueInstance(pagingLogger4.Object); + hc.EnqueueInstance(actionRunner as IActionRunner); + hc.SetSingleton(jobServerQueue.Object); + + var jobContext = new Runner.Worker.ExecutionContext(); + jobContext.Initialize(hc); + + // Act. + jobContext.InitializeJob(jobRequest, CancellationToken.None); + + var action = jobContext.CreateChild(Guid.NewGuid(), "action", "action", null, null, 0); + + var postRunner = hc.CreateService(); + postRunner.Action = new Pipelines.ActionStep() { Id = Guid.NewGuid(), Name = "post", DisplayName = "Post Action", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } }; + postRunner.Stage = ActionRunStage.Post; + postRunner.Condition = "always()"; + postRunner.DisplayName = "Post Action"; + + action.RegisterPostJobStep(postRunner); + + // Assert: action post-step telemetry is left for the handler to fill in, + // so RegisterPostJobStep should NOT pre-populate runner-owned defaults. + Assert.NotNull(postRunner.ExecutionContext); + Assert.Null(postRunner.ExecutionContext.StepTelemetry.Type); + Assert.Null(postRunner.ExecutionContext.StepTelemetry.Action); + } + } + [Fact] [Trait("Level", "L0")] [Trait("Category", "Worker")]