// Download all required actions. // Make sure all condition inputs are valid. // Build up three list of steps for jobrunner (pre-job, job, post-job). public async Task <List <IStep> > InitializeJob(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message) { Trace.Entering(); ArgUtil.NotNull(jobContext, nameof(jobContext)); ArgUtil.NotNull(message, nameof(message)); // Create a new timeline record for 'Set up job' IExecutionContext context = jobContext.CreateChild(Guid.NewGuid(), "Set up job", $"{nameof(JobExtension)}_Init", null, null); List <IStep> preJobSteps = new List <IStep>(); List <IStep> jobSteps = new List <IStep>(); using (var register = jobContext.CancellationToken.Register(() => { context.CancelToken(); })) { try { context.Start(); context.Debug($"Starting: Set up job"); context.Output($"Current runner version: '{BuildConstants.RunnerPackage.Version}'"); var setupInfoFile = HostContext.GetConfigFile(WellKnownConfigFile.SetupInfo); if (File.Exists(setupInfoFile)) { Trace.Info($"Load machine setup info from {setupInfoFile}"); try { var setupInfo = IOUtil.LoadObject <List <SetupInfo> >(setupInfoFile); if (setupInfo?.Count > 0) { foreach (var info in setupInfo) { if (!string.IsNullOrEmpty(info?.Detail)) { var groupName = info.Group; if (string.IsNullOrEmpty(groupName)) { groupName = "Machine Setup Info"; } context.Output($"##[group]{groupName}"); var multiLines = info.Detail.Replace("\r\n", "\n").TrimEnd('\n').Split('\n'); foreach (var line in multiLines) { context.Output(line); } context.Output("##[endgroup]"); } } } } catch (Exception ex) { context.Output($"Fail to load and print machine setup info: {ex.Message}"); Trace.Error(ex); } } var repoFullName = context.GetGitHubContext("repository"); ArgUtil.NotNull(repoFullName, nameof(repoFullName)); context.Debug($"Primary repository: {repoFullName}"); // Print proxy setting information for better diagnostic experience if (!string.IsNullOrEmpty(HostContext.WebProxy.HttpProxyAddress)) { context.Output($"Runner is running behind proxy server '{HostContext.WebProxy.HttpProxyAddress}' for all HTTP requests."); } if (!string.IsNullOrEmpty(HostContext.WebProxy.HttpsProxyAddress)) { context.Output($"Runner is running behind proxy server '{HostContext.WebProxy.HttpsProxyAddress}' for all HTTPS requests."); } // Prepare the workflow directory context.Output("Prepare workflow directory"); var directoryManager = HostContext.GetService <IPipelineDirectoryManager>(); TrackingConfig trackingConfig = directoryManager.PrepareDirectory( context, message.Workspace); // Set the directory variables context.Debug("Update context data"); string _workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); context.SetRunnerContext("workspace", Path.Combine(_workDirectory, trackingConfig.PipelineDirectory)); context.SetGitHubContext("workspace", Path.Combine(_workDirectory, trackingConfig.WorkspaceDirectory)); // Evaluate the job-level environment variables context.Debug("Evaluating job-level environment variables"); var templateEvaluator = context.ToPipelineTemplateEvaluator(); foreach (var token in message.EnvironmentVariables) { var environmentVariables = templateEvaluator.EvaluateStepEnvironment(token, jobContext.ExpressionValues, VarUtil.EnvironmentVariableKeyComparer); foreach (var pair in environmentVariables) { context.EnvironmentVariables[pair.Key] = pair.Value ?? string.Empty; context.SetEnvContext(pair.Key, pair.Value ?? string.Empty); } } // Evaluate the job container context.Debug("Evaluating job container"); var container = templateEvaluator.EvaluateJobContainer(message.JobContainer, jobContext.ExpressionValues); if (container != null) { jobContext.Container = new Container.ContainerInfo(HostContext, container); } // Evaluate the job service containers context.Debug("Evaluating job service containers"); var serviceContainers = templateEvaluator.EvaluateJobServiceContainers(message.JobServiceContainers, jobContext.ExpressionValues); if (serviceContainers?.Count > 0) { foreach (var pair in serviceContainers) { var networkAlias = pair.Key; var serviceContainer = pair.Value; jobContext.ServiceContainers.Add(new Container.ContainerInfo(HostContext, serviceContainer, false, networkAlias)); } } // Evaluate the job defaults context.Debug("Evaluating job defaults"); foreach (var token in message.Defaults) { var defaults = token.AssertMapping("defaults"); if (defaults.Any(x => string.Equals(x.Key.AssertString("defaults key").Value, "run", StringComparison.OrdinalIgnoreCase))) { context.JobDefaults["run"] = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); var defaultsRun = defaults.First(x => string.Equals(x.Key.AssertString("defaults key").Value, "run", StringComparison.OrdinalIgnoreCase)); var jobDefaults = templateEvaluator.EvaluateJobDefaultsRun(defaultsRun.Value, jobContext.ExpressionValues); foreach (var pair in jobDefaults) { if (!string.IsNullOrEmpty(pair.Value)) { context.JobDefaults["run"][pair.Key] = pair.Value; } } } } // Build up 2 lists of steps, pre-job, job // Download actions not already in the cache Trace.Info("Downloading actions"); var actionManager = HostContext.GetService <IActionManager>(); var prepareSteps = await actionManager.PrepareActionsAsync(context, message.Steps); preJobSteps.AddRange(prepareSteps); // Add start-container steps, record and stop-container steps if (jobContext.Container != null || jobContext.ServiceContainers.Count > 0) { var containerProvider = HostContext.GetService <IContainerOperationProvider>(); var containers = new List <Container.ContainerInfo>(); if (jobContext.Container != null) { containers.Add(jobContext.Container); } containers.AddRange(jobContext.ServiceContainers); preJobSteps.Add(new JobExtensionRunner(runAsync: containerProvider.StartContainersAsync, condition: $"{PipelineTemplateConstants.Success}()", displayName: "Initialize containers", data: (object)containers)); } // Add action steps foreach (var step in message.Steps) { if (step.Type == Pipelines.StepType.Action) { var action = step as Pipelines.ActionStep; Trace.Info($"Adding {action.DisplayName}."); var actionRunner = HostContext.CreateService <IActionRunner>(); actionRunner.Action = action; actionRunner.Stage = ActionRunStage.Main; actionRunner.Condition = step.Condition; var contextData = new Pipelines.ContextData.DictionaryContextData(); if (message.ContextData?.Count > 0) { foreach (var pair in message.ContextData) { contextData[pair.Key] = pair.Value; } } actionRunner.TryEvaluateDisplayName(contextData, context); jobSteps.Add(actionRunner); } } // Create execution context for pre-job steps foreach (var step in preJobSteps) { if (step is JobExtensionRunner) { JobExtensionRunner extensionStep = step as JobExtensionRunner; ArgUtil.NotNull(extensionStep, extensionStep.DisplayName); Guid stepId = Guid.NewGuid(); extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, null, null, stepId.ToString("N")); } } // Create execution context for job steps foreach (var step in jobSteps) { if (step is IActionRunner actionStep) { ArgUtil.NotNull(actionStep, step.DisplayName); actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, actionStep.Action.ScopeName, actionStep.Action.ContextName); } } List <IStep> steps = new List <IStep>(); steps.AddRange(preJobSteps); steps.AddRange(jobSteps); // Prepare for orphan process cleanup _processCleanup = jobContext.Variables.GetBoolean("process.clean") ?? true; if (_processCleanup) { // Set the RUNNER_TRACKING_ID env variable. Environment.SetEnvironmentVariable(Constants.ProcessTrackingId, _processLookupId); context.Debug("Collect running processes for tracking orphan processes."); // Take a snapshot of current running processes Dictionary <int, Process> processes = SnapshotProcesses(); foreach (var proc in processes) { // Pid_ProcessName _existingProcesses.Add($"{proc.Key}_{proc.Value.ProcessName}"); } } return(steps); } catch (OperationCanceledException ex) when(jobContext.CancellationToken.IsCancellationRequested) { // Log the exception and cancel the JobExtension Initialization. Trace.Error($"Caught cancellation exception from JobExtension Initialization: {ex}"); context.Error(ex); context.Result = TaskResult.Canceled; throw; } catch (Exception ex) { // Log the error and fail the JobExtension Initialization. Trace.Error($"Caught exception from JobExtension Initialization: {ex}"); context.Error(ex); context.Result = TaskResult.Failed; throw; } finally { context.Debug("Finishing: Set up job"); context.Complete(); } } }
// Download all required actions. // Make sure all condition inputs are valid. // Build up three list of steps for jobrunner (pre-job, job, post-job). public async Task <List <IStep> > InitializeJob(IExecutionContext jobContext, Pipelines.AgentJobRequestMessage message) { Trace.Entering(); ArgUtil.NotNull(jobContext, nameof(jobContext)); ArgUtil.NotNull(message, nameof(message)); // Create a new timeline record for 'Set up job' IExecutionContext context = jobContext.CreateChild(Guid.NewGuid(), "Set up job", $"{nameof(JobExtension)}_Init", null, null); List <IStep> preJobSteps = new List <IStep>(); List <IStep> jobSteps = new List <IStep>(); using (var register = jobContext.CancellationToken.Register(() => { context.CancelToken(); })) { try { context.Start(); context.Debug($"Starting: Set up job"); context.Output($"Current runner version: '{BuildConstants.RunnerPackage.Version}'"); var setting = HostContext.GetService <IConfigurationStore>().GetSettings(); var credFile = HostContext.GetConfigFile(WellKnownConfigFile.Credentials); if (File.Exists(credFile)) { var credData = IOUtil.LoadObject <CredentialData>(credFile); if (credData != null && credData.Data.TryGetValue("clientId", out var clientId)) { // print out HostName for self-hosted runner context.Output($"Runner name: '{setting.AgentName}'"); if (message.Variables.TryGetValue("system.runnerGroupName", out VariableValue runnerGroupName)) { context.Output($"Runner group name: '{runnerGroupName.Value}'"); } context.Output($"Machine name: '{Environment.MachineName}'"); } } var setupInfoFile = HostContext.GetConfigFile(WellKnownConfigFile.SetupInfo); if (File.Exists(setupInfoFile)) { Trace.Info($"Load machine setup info from {setupInfoFile}"); try { var setupInfo = IOUtil.LoadObject <List <SetupInfo> >(setupInfoFile); if (setupInfo?.Count > 0) { foreach (var info in setupInfo) { if (!string.IsNullOrEmpty(info?.Detail)) { var groupName = info.Group; if (string.IsNullOrEmpty(groupName)) { groupName = "Machine Setup Info"; } context.Output($"##[group]{groupName}"); var multiLines = info.Detail.Replace("\r\n", "\n").TrimEnd('\n').Split('\n'); foreach (var line in multiLines) { context.Output(line); } context.Output("##[endgroup]"); } } } } catch (Exception ex) { context.Output($"Fail to load and print machine setup info: {ex.Message}"); Trace.Error(ex); } } try { var tokenPermissions = jobContext.Global.Variables.Get("system.github.token.permissions") ?? ""; if (!string.IsNullOrEmpty(tokenPermissions)) { context.Output($"##[group]GITHUB_TOKEN Permissions"); var permissions = StringUtil.ConvertFromJson <Dictionary <string, string> >(tokenPermissions); foreach (KeyValuePair <string, string> entry in permissions) { context.Output($"{entry.Key}: {entry.Value}"); } context.Output("##[endgroup]"); } } catch (Exception ex) { context.Output($"Fail to parse and display GITHUB_TOKEN permissions list: {ex.Message}"); Trace.Error(ex); } var repoFullName = context.GetGitHubContext("repository"); ArgUtil.NotNull(repoFullName, nameof(repoFullName)); context.Debug($"Primary repository: {repoFullName}"); // Print proxy setting information for better diagnostic experience if (!string.IsNullOrEmpty(HostContext.WebProxy.HttpProxyAddress)) { context.Output($"Runner is running behind proxy server '{HostContext.WebProxy.HttpProxyAddress}' for all HTTP requests."); } if (!string.IsNullOrEmpty(HostContext.WebProxy.HttpsProxyAddress)) { context.Output($"Runner is running behind proxy server '{HostContext.WebProxy.HttpsProxyAddress}' for all HTTPS requests."); } // Prepare the workflow directory context.Output("Prepare workflow directory"); var directoryManager = HostContext.GetService <IPipelineDirectoryManager>(); TrackingConfig trackingConfig = directoryManager.PrepareDirectory( context, message.Workspace); // Set the directory variables context.Debug("Update context data"); string _workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work); context.SetRunnerContext("workspace", Path.Combine(_workDirectory, trackingConfig.PipelineDirectory)); context.SetGitHubContext("workspace", Path.Combine(_workDirectory, trackingConfig.WorkspaceDirectory)); // Temporary hack for GHES alpha var configurationStore = HostContext.GetService <IConfigurationStore>(); var runnerSettings = configurationStore.GetSettings(); if (string.IsNullOrEmpty(context.GetGitHubContext("server_url")) && !runnerSettings.IsHostedServer && !string.IsNullOrEmpty(runnerSettings.GitHubUrl)) { var url = new Uri(runnerSettings.GitHubUrl); var portInfo = url.IsDefaultPort ? string.Empty : $":{url.Port.ToString(CultureInfo.InvariantCulture)}"; context.SetGitHubContext("server_url", $"{url.Scheme}://{url.Host}{portInfo}"); context.SetGitHubContext("api_url", $"{url.Scheme}://{url.Host}{portInfo}/api/v3"); context.SetGitHubContext("graphql_url", $"{url.Scheme}://{url.Host}{portInfo}/api/graphql"); } // Evaluate the job-level environment variables context.Debug("Evaluating job-level environment variables"); var templateEvaluator = context.ToPipelineTemplateEvaluator(); foreach (var token in message.EnvironmentVariables) { var environmentVariables = templateEvaluator.EvaluateStepEnvironment(token, jobContext.ExpressionValues, jobContext.ExpressionFunctions, VarUtil.EnvironmentVariableKeyComparer); foreach (var pair in environmentVariables) { context.Global.EnvironmentVariables[pair.Key] = pair.Value ?? string.Empty; context.SetEnvContext(pair.Key, pair.Value ?? string.Empty); } } // Evaluate the job container context.Debug("Evaluating job container"); var container = templateEvaluator.EvaluateJobContainer(message.JobContainer, jobContext.ExpressionValues, jobContext.ExpressionFunctions); if (container != null) { jobContext.Global.Container = new Container.ContainerInfo(HostContext, container); } // Evaluate the job service containers context.Debug("Evaluating job service containers"); var serviceContainers = templateEvaluator.EvaluateJobServiceContainers(message.JobServiceContainers, jobContext.ExpressionValues, jobContext.ExpressionFunctions); if (serviceContainers?.Count > 0) { foreach (var pair in serviceContainers) { var networkAlias = pair.Key; var serviceContainer = pair.Value; jobContext.Global.ServiceContainers.Add(new Container.ContainerInfo(HostContext, serviceContainer, false, networkAlias)); } } // Evaluate the job defaults context.Debug("Evaluating job defaults"); foreach (var token in message.Defaults) { var defaults = token.AssertMapping("defaults"); if (defaults.Any(x => string.Equals(x.Key.AssertString("defaults key").Value, "run", StringComparison.OrdinalIgnoreCase))) { context.Global.JobDefaults["run"] = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); var defaultsRun = defaults.First(x => string.Equals(x.Key.AssertString("defaults key").Value, "run", StringComparison.OrdinalIgnoreCase)); var jobDefaults = templateEvaluator.EvaluateJobDefaultsRun(defaultsRun.Value, jobContext.ExpressionValues, jobContext.ExpressionFunctions); foreach (var pair in jobDefaults) { if (!string.IsNullOrEmpty(pair.Value)) { context.Global.JobDefaults["run"][pair.Key] = pair.Value; } } } } // Build up 2 lists of steps, pre-job, job // Download actions not already in the cache Trace.Info("Downloading actions"); var actionManager = HostContext.GetService <IActionManager>(); var prepareResult = await actionManager.PrepareActionsAsync(context, message.Steps); preJobSteps.AddRange(prepareResult.ContainerSetupSteps); // Add start-container steps, record and stop-container steps if (jobContext.Global.Container != null || jobContext.Global.ServiceContainers.Count > 0) { var containerProvider = HostContext.GetService <IContainerOperationProvider>(); var containers = new List <Container.ContainerInfo>(); if (jobContext.Global.Container != null) { containers.Add(jobContext.Global.Container); } containers.AddRange(jobContext.Global.ServiceContainers); preJobSteps.Add(new JobExtensionRunner(runAsync: containerProvider.StartContainersAsync, condition: $"{PipelineTemplateConstants.Success}()", displayName: "Initialize containers", data: (object)containers)); } // Add action steps foreach (var step in message.Steps) { if (step.Type == Pipelines.StepType.Action) { var action = step as Pipelines.ActionStep; Trace.Info($"Adding {action.DisplayName}."); var actionRunner = HostContext.CreateService <IActionRunner>(); actionRunner.Action = action; actionRunner.Stage = ActionRunStage.Main; actionRunner.Condition = step.Condition; var contextData = new Pipelines.ContextData.DictionaryContextData(); if (message.ContextData?.Count > 0) { foreach (var pair in message.ContextData) { contextData[pair.Key] = pair.Value; } } actionRunner.TryEvaluateDisplayName(contextData, context); jobSteps.Add(actionRunner); if (prepareResult.PreStepTracker.TryGetValue(step.Id, out var preStep)) { Trace.Info($"Adding pre-{action.DisplayName}."); preStep.TryEvaluateDisplayName(contextData, context); preStep.DisplayName = $"Pre {preStep.DisplayName}"; preJobSteps.Add(preStep); } } } var intraActionStates = new Dictionary <Guid, Dictionary <string, string> >(); foreach (var preStep in prepareResult.PreStepTracker) { intraActionStates[preStep.Key] = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); } // Create execution context for pre-job steps foreach (var step in preJobSteps) { if (step is JobExtensionRunner) { JobExtensionRunner extensionStep = step as JobExtensionRunner; ArgUtil.NotNull(extensionStep, extensionStep.DisplayName); Guid stepId = Guid.NewGuid(); extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, null, null, stepId.ToString("N")); } else if (step is IActionRunner actionStep) { ArgUtil.NotNull(actionStep, step.DisplayName); Guid stepId = Guid.NewGuid(); actionStep.ExecutionContext = jobContext.CreateChild(stepId, actionStep.DisplayName, stepId.ToString("N"), null, null, intraActionStates[actionStep.Action.Id]); } } // Create execution context for job steps foreach (var step in jobSteps) { if (step is IActionRunner actionStep) { ArgUtil.NotNull(actionStep, step.DisplayName); intraActionStates.TryGetValue(actionStep.Action.Id, out var intraActionState); actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, null, actionStep.Action.ContextName, intraActionState); } } List <IStep> steps = new List <IStep>(); steps.AddRange(preJobSteps); steps.AddRange(jobSteps); // Prepare for orphan process cleanup _processCleanup = jobContext.Global.Variables.GetBoolean("process.clean") ?? true; if (_processCleanup) { // Set the RUNNER_TRACKING_ID env variable. Environment.SetEnvironmentVariable(Constants.ProcessTrackingId, _processLookupId); context.Debug("Collect running processes for tracking orphan processes."); // Take a snapshot of current running processes Dictionary <int, Process> processes = SnapshotProcesses(); foreach (var proc in processes) { // Pid_ProcessName _existingProcesses.Add($"{proc.Key}_{proc.Value.ProcessName}"); } } jobContext.Global.EnvironmentVariables.TryGetValue(Constants.Runner.Features.DiskSpaceWarning, out var enableWarning); if (StringUtil.ConvertToBoolean(enableWarning, defaultValue: true)) { _diskSpaceCheckTask = CheckDiskSpaceAsync(context, _diskSpaceCheckToken.Token); } return(steps); } catch (OperationCanceledException ex) when(jobContext.CancellationToken.IsCancellationRequested) { // Log the exception and cancel the JobExtension Initialization. Trace.Error($"Caught cancellation exception from JobExtension Initialization: {ex}"); context.Error(ex); context.Result = TaskResult.Canceled; throw; } catch (FailedToResolveActionDownloadInfoException ex) { // Log the error and fail the JobExtension Initialization. Trace.Error($"Caught exception from JobExtenion Initialization: {ex}"); context.InfrastructureError(ex.Message); context.Result = TaskResult.Failed; throw; } catch (Exception ex) { // Log the error and fail the JobExtension Initialization. Trace.Error($"Caught exception from JobExtension Initialization: {ex}"); context.Error(ex); context.Result = TaskResult.Failed; throw; } finally { context.Debug("Finishing: Set up job"); context.Complete(); } } }
public override Object ReadJson( JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) { switch (reader.TokenType) { case JsonToken.String: return(new StringContextData(reader.Value.ToString())); case JsonToken.Boolean: return(new BooleanContextData((Boolean)reader.Value)); case JsonToken.Float: return(new NumberContextData((Double)reader.Value)); case JsonToken.Integer: return(new NumberContextData((Double)(Int64)reader.Value)); case JsonToken.StartObject: break; default: return(null); } Int32? type = null; JObject value = JObject.Load(reader); if (!value.TryGetValue("t", StringComparison.OrdinalIgnoreCase, out JToken typeValue)) { type = PipelineContextDataType.String; } else if (typeValue.Type == JTokenType.Integer) { type = (Int32)typeValue; } else { return(existingValue); } Object newValue = null; switch (type) { case PipelineContextDataType.String: newValue = new StringContextData(null); break; case PipelineContextDataType.Array: newValue = new ArrayContextData(); break; case PipelineContextDataType.Dictionary: newValue = new DictionaryContextData(); break; case PipelineContextDataType.Boolean: newValue = new BooleanContextData(false); break; case PipelineContextDataType.Number: newValue = new NumberContextData(0); break; case PipelineContextDataType.CaseSensitiveDictionary: newValue = new CaseSensitiveDictionaryContextData(); break; default: throw new NotSupportedException($"Unexpected {nameof(PipelineContextDataType)} '{type}'"); } if (value != null) { using (JsonReader objectReader = value.CreateReader()) { serializer.Populate(objectReader, newValue); } } return(newValue); }