diff --git a/src/Runner.Common/Constants.cs b/src/Runner.Common/Constants.cs index a9f91223a..24166646c 100644 --- a/src/Runner.Common/Constants.cs +++ b/src/Runner.Common/Constants.cs @@ -163,6 +163,7 @@ namespace GitHub.Runner.Common public static readonly string LogTemplateErrorsAsDebugMessages = "DistributedTask.LogTemplateErrorsAsDebugMessages"; public static readonly string UseContainerPathForTemplate = "DistributedTask.UseContainerPathForTemplate"; public static readonly string AllowRunnerContainerHooks = "DistributedTask.AllowRunnerContainerHooks"; + public static readonly string AddCheckRunIdToJobContext = "actions_add_check_run_id_to_job_context"; } public static readonly string InternalTelemetryIssueDataKey = "_internal_telemetry"; diff --git a/src/Runner.Worker/ExecutionContext.cs b/src/Runner.Worker/ExecutionContext.cs index dfe925230..e64c6e24a 100644 --- a/src/Runner.Worker/ExecutionContext.cs +++ b/src/Runner.Worker/ExecutionContext.cs @@ -862,7 +862,21 @@ namespace GitHub.Runner.Worker ExpressionValues["secrets"] = Global.Variables.ToSecretsContext(); ExpressionValues["runner"] = new RunnerContext(); - ExpressionValues["job"] = new JobContext(); + + Trace.Info("Initializing Job context"); + var jobContext = new JobContext(); + if (Global.Variables.GetBoolean(Constants.Runner.Features.AddCheckRunIdToJobContext) ?? false) + { + ExpressionValues.TryGetValue("job", out var jobDictionary); + if (jobDictionary != null) + { + foreach (var pair in jobDictionary.AssertDictionary("job")) + { + jobContext[pair.Key] = pair.Value; + } + } + } + ExpressionValues["job"] = jobContext; Trace.Info("Initialize GitHub context"); var githubAccessToken = new StringContextData(Global.Variables.Get("system.github.token")); diff --git a/src/Runner.Worker/JobContext.cs b/src/Runner.Worker/JobContext.cs index e3760560f..09f3296de 100644 --- a/src/Runner.Worker/JobContext.cs +++ b/src/Runner.Worker/JobContext.cs @@ -1,4 +1,4 @@ -using GitHub.DistributedTask.Pipelines.ContextData; +using GitHub.DistributedTask.Pipelines.ContextData; using GitHub.Runner.Common.Util; using GitHub.Runner.Common; @@ -56,5 +56,31 @@ namespace GitHub.Runner.Worker } } } + + public double? CheckRunId + { + get + { + if (this.TryGetValue("check_run_id", out var value) && value is NumberContextData number) + { + return number.Value; + } + else + { + return null; + } + } + set + { + if (value.HasValue) + { + this["check_run_id"] = new NumberContextData(value.Value); + } + else + { + this["check_run_id"] = null; + } + } + } } } diff --git a/src/Test/L0/Worker/ExecutionContextL0.cs b/src/Test/L0/Worker/ExecutionContextL0.cs index 7357212d1..2f28f797f 100644 --- a/src/Test/L0/Worker/ExecutionContextL0.cs +++ b/src/Test/L0/Worker/ExecutionContextL0.cs @@ -1168,6 +1168,77 @@ namespace GitHub.Runner.Common.Tests.Worker } } + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public void InitializeJob_HydratesJobContextWithCheckRunId() + { + using (TestHostContext hc = CreateTestContext()) + { + // Arrange: Create a job request message and make sure the feature flag is enabled + var variables = new Dictionary() + { + [Constants.Runner.Features.AddCheckRunIdToJobContext] = new VariableValue("true"), + }; + var jobRequest = new Pipelines.AgentJobRequestMessage(new TaskOrchestrationPlanReference(), new TimelineReference(), Guid.NewGuid(), "some job name", "some job name", null, null, null, variables, new List(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List(), null, null, null, null, null); + var pagingLogger = new Moq.Mock(); + var jobServerQueue = new Moq.Mock(); + hc.EnqueueInstance(pagingLogger.Object); + hc.SetSingleton(jobServerQueue.Object); + var ec = new Runner.Worker.ExecutionContext(); + ec.Initialize(hc); + + // Arrange: Add check_run_id to the job context + var jobContext = new Pipelines.ContextData.DictionaryContextData(); + jobContext["check_run_id"] = new NumberContextData(123456); + jobRequest.ContextData["job"] = jobContext; + jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData(); + + // Act + ec.InitializeJob(jobRequest, CancellationToken.None); + + // Assert + Assert.NotNull(ec.JobContext); + Assert.Equal(123456, ec.JobContext.CheckRunId); + } + } + + // TODO: this test can be deleted when `AddCheckRunIdToJobContext` is fully rolled out + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public void InitializeJob_HydratesJobContextWithCheckRunId_FeatureFlagDisabled() + { + using (TestHostContext hc = CreateTestContext()) + { + // Arrange: Create a job request message and make sure the feature flag is disabled + var variables = new Dictionary() + { + [Constants.Runner.Features.AddCheckRunIdToJobContext] = new VariableValue("false"), + }; + var jobRequest = new Pipelines.AgentJobRequestMessage(new TaskOrchestrationPlanReference(), new TimelineReference(), Guid.NewGuid(), "some job name", "some job name", null, null, null, variables, new List(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List(), null, null, null, null, null); + var pagingLogger = new Moq.Mock(); + var jobServerQueue = new Moq.Mock(); + hc.EnqueueInstance(pagingLogger.Object); + hc.SetSingleton(jobServerQueue.Object); + var ec = new Runner.Worker.ExecutionContext(); + ec.Initialize(hc); + + // Arrange: Add check_run_id to the job context + var jobContext = new Pipelines.ContextData.DictionaryContextData(); + jobContext["check_run_id"] = new NumberContextData(123456); + jobRequest.ContextData["job"] = jobContext; + jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData(); + + // Act + ec.InitializeJob(jobRequest, CancellationToken.None); + + // Assert + Assert.NotNull(ec.JobContext); + Assert.Null(ec.JobContext.CheckRunId); // with the feature flag disabled we should not have added a CheckRunId to the JobContext + } + } + private bool ExpressionValuesAssertEqual(DictionaryContextData expect, DictionaryContextData actual) { foreach (var key in expect.Keys.ToList()) diff --git a/src/Test/L0/Worker/JobContextL0.cs b/src/Test/L0/Worker/JobContextL0.cs new file mode 100644 index 000000000..87e334379 --- /dev/null +++ b/src/Test/L0/Worker/JobContextL0.cs @@ -0,0 +1,38 @@ +using System; +using GitHub.DistributedTask.Pipelines.ContextData; +using GitHub.Runner.Worker; +using Xunit; + +namespace GitHub.Runner.Common.Tests.Worker +{ + public class JobContextL0 + { + [Fact] + public void CheckRunId_SetAndGet_WorksCorrectly() + { + var ctx = new JobContext(); + ctx.CheckRunId = 12345; + Assert.Equal(12345, ctx.CheckRunId); + Assert.True(ctx.TryGetValue("check_run_id", out var value)); + Assert.IsType(value); + Assert.Equal(12345, ((NumberContextData)value).Value); + } + + [Fact] + public void CheckRunId_NotSet_ReturnsNull() + { + var ctx = new JobContext(); + Assert.Null(ctx.CheckRunId); + Assert.False(ctx.TryGetValue("check_run_id", out var value)); + } + + [Fact] + public void CheckRunId_SetNull_RemovesKey() + { + var ctx = new JobContext(); + ctx.CheckRunId = 12345; + ctx.CheckRunId = null; + Assert.Null(ctx.CheckRunId); + } + } +}