From d5c7097d2c4f3f40a029124bbf1bc4c9994929f5 Mon Sep 17 00:00:00 2001 From: Tingluo Huang Date: Mon, 13 Apr 2020 21:46:30 -0400 Subject: [PATCH] support 'pre' execution for actions. (#389) --- src/Runner.Worker/ActionManager.cs | 84 ++++++-- src/Runner.Worker/ActionManifestManager.cs | 22 +- src/Runner.Worker/ActionRunner.cs | 23 +-- .../ContainerOperationProvider.cs | 2 +- src/Runner.Worker/ExecutionContext.cs | 23 ++- .../Handlers/ContainerActionHandler.cs | 6 +- .../Handlers/NodeScriptActionHandler.cs | 6 +- .../Handlers/RunnerPluginHandler.cs | 2 +- src/Runner.Worker/JobExtension.cs | 27 ++- src/Runner.Worker/action_yaml.json | 4 + src/Test/L0/Listener/MessageListenerL0.cs | 2 +- src/Test/L0/Worker/ActionManagerL0.cs | 126 ++++++++++-- src/Test/L0/Worker/ActionManifestManagerL0.cs | 188 +++++++++++++++++- src/Test/L0/Worker/ExecutionContextL0.cs | 93 ++++++++- src/Test/L0/Worker/JobExtensionL0.cs | 4 +- src/Test/TestData/dockerfileaction_init.yml | 27 +++ .../dockerfileaction_init_default.yml | 26 +++ src/Test/TestData/nodeaction_init.yml | 22 ++ src/Test/TestData/nodeaction_init_default.yml | 21 ++ 19 files changed, 640 insertions(+), 68 deletions(-) create mode 100644 src/Test/TestData/dockerfileaction_init.yml create mode 100644 src/Test/TestData/dockerfileaction_init_default.yml create mode 100644 src/Test/TestData/nodeaction_init.yml create mode 100644 src/Test/TestData/nodeaction_init_default.yml diff --git a/src/Runner.Worker/ActionManager.cs b/src/Runner.Worker/ActionManager.cs index 4d01a7894..533841659 100644 --- a/src/Runner.Worker/ActionManager.cs +++ b/src/Runner.Worker/ActionManager.cs @@ -21,11 +21,24 @@ using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplat namespace GitHub.Runner.Worker { + public class PrepareResult + { + public PrepareResult(List containerSetupSteps, Dictionary preStepTracker) + { + this.ContainerSetupSteps = containerSetupSteps; + this.PreStepTracker = preStepTracker; + } + + public List ContainerSetupSteps { get; set; } + + public Dictionary PreStepTracker { get; set; } + } + [ServiceLocator(Default = typeof(ActionManager))] public interface IActionManager : IRunnerService { Dictionary CachedActionContainers { get; } - Task> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable steps); + Task PrepareActionsAsync(IExecutionContext executionContext, IEnumerable steps); Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action); } @@ -39,7 +52,7 @@ namespace GitHub.Runner.Worker private readonly Dictionary _cachedActionContainers = new Dictionary(); public Dictionary CachedActionContainers => _cachedActionContainers; - public async Task> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable steps) + public async Task PrepareActionsAsync(IExecutionContext executionContext, IEnumerable steps) { ArgUtil.NotNull(executionContext, nameof(executionContext)); ArgUtil.NotNull(steps, nameof(steps)); @@ -49,6 +62,7 @@ namespace GitHub.Runner.Worker Dictionary> imagesToBuild = new Dictionary>(StringComparer.OrdinalIgnoreCase); Dictionary imagesToBuildInfo = new Dictionary(StringComparer.OrdinalIgnoreCase); List containerSetupSteps = new List(); + Dictionary preStepTracker = new Dictionary(); IEnumerable actions = steps.OfType(); // TODO: Deprecate the PREVIEW_ACTION_TOKEN @@ -117,6 +131,22 @@ namespace GitHub.Runner.Worker imagesToBuildInfo[setupInfo.ActionRepository] = setupInfo; } } + + var repoAction = action.Reference as Pipelines.RepositoryPathReference; + if (repoAction.RepositoryType != Pipelines.PipelineConstants.SelfAlias) + { + var definition = LoadAction(executionContext, action); + if (definition.Data.Execution.HasPre) + { + var actionRunner = HostContext.CreateService(); + actionRunner.Action = action; + actionRunner.Stage = ActionRunStage.Pre; + actionRunner.Condition = definition.Data.Execution.InitCondition; + + Trace.Info($"Add 'pre' execution for {action.Id}"); + preStepTracker[action.Id] = actionRunner; + } + } } } @@ -153,7 +183,7 @@ namespace GitHub.Runner.Worker } #endif - return containerSetupSteps; + return new PrepareResult(containerSetupSteps, preStepTracker); } public Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action) @@ -245,14 +275,19 @@ namespace GitHub.Runner.Worker Trace.Info($"Action container env: {StringUtil.ConvertToJson(containerAction.Environment)}."); } + if (!string.IsNullOrEmpty(containerAction.Pre)) + { + Trace.Info($"Action container pre entrypoint: {containerAction.Pre}."); + } + if (!string.IsNullOrEmpty(containerAction.EntryPoint)) { Trace.Info($"Action container entrypoint: {containerAction.EntryPoint}."); } - if (!string.IsNullOrEmpty(containerAction.Cleanup)) + if (!string.IsNullOrEmpty(containerAction.Post)) { - Trace.Info($"Action container cleanup entrypoint: {containerAction.Cleanup}."); + Trace.Info($"Action container post entrypoint: {containerAction.Post}."); } if (CachedActionContainers.TryGetValue(action.Id, out var container)) @@ -264,8 +299,9 @@ namespace GitHub.Runner.Worker else if (definition.Data.Execution.ExecutionType == ActionExecutionType.NodeJS) { var nodeAction = definition.Data.Execution as NodeJSActionExecutionData; + Trace.Info($"Action pre node.js file: {nodeAction.Pre ?? "N/A"}."); Trace.Info($"Action node.js file: {nodeAction.Script}."); - Trace.Info($"Action cleanup node.js file: {nodeAction.Cleanup ?? "N/A"}."); + Trace.Info($"Action post node.js file: {nodeAction.Post ?? "N/A"}."); } else if (definition.Data.Execution.ExecutionType == ActionExecutionType.Plugin) { @@ -281,7 +317,7 @@ namespace GitHub.Runner.Worker if (!string.IsNullOrEmpty(plugin.PostPluginTypeName)) { - pluginAction.Cleanup = plugin.PostPluginTypeName; + pluginAction.Post = plugin.PostPluginTypeName; Trace.Info($"Action cleanup plugin: {plugin.PluginTypeName}."); } } @@ -788,7 +824,8 @@ namespace GitHub.Runner.Worker { public override ActionExecutionType ExecutionType => ActionExecutionType.Container; - public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup); + public override bool HasPre => !string.IsNullOrEmpty(Pre); + public override bool HasPost => !string.IsNullOrEmpty(Post); public string Image { get; set; } @@ -798,51 +835,66 @@ namespace GitHub.Runner.Worker public MappingToken Environment { get; set; } - public string Cleanup { get; set; } + public string Pre { get; set; } + + public string Post { get; set; } } public sealed class NodeJSActionExecutionData : ActionExecutionData { public override ActionExecutionType ExecutionType => ActionExecutionType.NodeJS; - public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup); + public override bool HasPre => !string.IsNullOrEmpty(Pre); + public override bool HasPost => !string.IsNullOrEmpty(Post); public string Script { get; set; } - public string Cleanup { get; set; } + public string Pre { get; set; } + + public string Post { get; set; } } public sealed class PluginActionExecutionData : ActionExecutionData { public override ActionExecutionType ExecutionType => ActionExecutionType.Plugin; - public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup); + public override bool HasPre => false; + + public override bool HasPost => !string.IsNullOrEmpty(Post); public string Plugin { get; set; } - public string Cleanup { get; set; } + public string Post { get; set; } } public sealed class ScriptActionExecutionData : ActionExecutionData { public override ActionExecutionType ExecutionType => ActionExecutionType.Script; - - public override bool HasCleanup => false; + public override bool HasPre => false; + public override bool HasPost => false; } public abstract class ActionExecutionData { + private string _initCondition = $"{Constants.Expressions.Always}()"; private string _cleanupCondition = $"{Constants.Expressions.Always}()"; public abstract ActionExecutionType ExecutionType { get; } - public abstract bool HasCleanup { get; } + public abstract bool HasPre { get; } + public abstract bool HasPost { get; } public string CleanupCondition { get { return _cleanupCondition; } set { _cleanupCondition = value; } } + + public string InitCondition + { + get { return _initCondition; } + set { _initCondition = value; } + } } public class ContainerSetupInfo diff --git a/src/Runner.Worker/ActionManifestManager.cs b/src/Runner.Worker/ActionManifestManager.cs index 9b94faaf8..4e9149d26 100644 --- a/src/Runner.Worker/ActionManifestManager.cs +++ b/src/Runner.Worker/ActionManifestManager.cs @@ -305,6 +305,9 @@ namespace GitHub.Runner.Worker var envToken = default(MappingToken); var mainToken = default(StringToken); var pluginToken = default(StringToken); + var preToken = default(StringToken); + var preEntrypointToken = default(StringToken); + var preIfToken = default(StringToken); var postToken = default(StringToken); var postEntrypointToken = default(StringToken); var postIfToken = default(StringToken); @@ -343,6 +346,15 @@ namespace GitHub.Runner.Worker case "post-if": postIfToken = run.Value.AssertString("post-if"); break; + case "pre": + preToken = run.Value.AssertString("pre"); + break; + case "pre-entrypoint": + preEntrypointToken = run.Value.AssertString("pre-entrypoint"); + break; + case "pre-if": + preIfToken = run.Value.AssertString("pre-if"); + break; default: Trace.Info($"Ignore run property {runsKey}."); break; @@ -365,7 +377,9 @@ namespace GitHub.Runner.Worker Arguments = argsToken, EntryPoint = entrypointToken?.Value, Environment = envToken, - Cleanup = postEntrypointToken?.Value, + Pre = preEntrypointToken?.Value, + InitCondition = preIfToken?.Value ?? "always()", + Post = postEntrypointToken?.Value, CleanupCondition = postIfToken?.Value ?? "always()" }; } @@ -374,14 +388,16 @@ namespace GitHub.Runner.Worker { if (string.IsNullOrEmpty(mainToken?.Value)) { - throw new ArgumentNullException($"Entry javascript fils is not provided."); + throw new ArgumentNullException($"Entry javascript file is not provided."); } else { return new NodeJSActionExecutionData() { Script = mainToken.Value, - Cleanup = postToken?.Value, + Pre = preToken?.Value, + InitCondition = preIfToken?.Value ?? "always()", + Post = postToken?.Value, CleanupCondition = postIfToken?.Value ?? "always()" }; } diff --git a/src/Runner.Worker/ActionRunner.cs b/src/Runner.Worker/ActionRunner.cs index 7272303bc..8331e9915 100644 --- a/src/Runner.Worker/ActionRunner.cs +++ b/src/Runner.Worker/ActionRunner.cs @@ -18,6 +18,7 @@ namespace GitHub.Runner.Worker { public enum ActionRunStage { + Pre, Main, Post, } @@ -81,20 +82,18 @@ namespace GitHub.Runner.Worker ActionExecutionData handlerData = definition.Data?.Execution; ArgUtil.NotNull(handlerData, nameof(handlerData)); + if (handlerData.HasPre && + Action.Reference is Pipelines.RepositoryPathReference repoAction && + string.Equals(repoAction.RepositoryType, Pipelines.PipelineConstants.SelfAlias, StringComparison.OrdinalIgnoreCase)) + { + ExecutionContext.Warning($"`pre` execution is not supported for local action from '{repoAction.Path}'"); + } + // The action has post cleanup defined. // we need to create timeline record for them and add them to the step list that StepRunner is using - if (handlerData.HasCleanup && Stage == ActionRunStage.Main) + if (handlerData.HasPost && (Stage == ActionRunStage.Pre || Stage == ActionRunStage.Main)) { - string postDisplayName = null; - if (this.DisplayName.StartsWith(PipelineTemplateConstants.RunDisplayPrefix)) - { - postDisplayName = $"Post {this.DisplayName.Substring(PipelineTemplateConstants.RunDisplayPrefix.Length)}"; - } - else - { - postDisplayName = $"Post {this.DisplayName}"; - } - + string postDisplayName = $"Post {this.DisplayName}"; var repositoryReference = Action.Reference as RepositoryPathReference; var pathString = string.IsNullOrEmpty(repositoryReference.Path) ? string.Empty : $"/{repositoryReference.Path}"; var repoString = string.IsNullOrEmpty(repositoryReference.Ref) ? $"{repositoryReference.Name}{pathString}" : @@ -108,7 +107,7 @@ namespace GitHub.Runner.Worker actionRunner.Condition = handlerData.CleanupCondition; actionRunner.DisplayName = postDisplayName; - ExecutionContext.RegisterPostJobStep($"{actionRunner.Action.Name}_post", actionRunner); + ExecutionContext.RegisterPostJobStep(actionRunner); } IStepHost stepHost = HostContext.CreateService(); diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index c105e2162..2a27a731a 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -49,7 +49,7 @@ namespace GitHub.Runner.Worker data: data); executionContext.Debug($"Register post job cleanup for stopping/deleting containers."); - executionContext.RegisterPostJobStep(nameof(StopContainersAsync), postJobStep); + executionContext.RegisterPostJobStep(postJobStep); // Check whether we are inside a container. // Our container feature requires to map working directory from host to the container. diff --git a/src/Runner.Worker/ExecutionContext.cs b/src/Runner.Worker/ExecutionContext.cs index 25cbeefb1..48c8d096f 100644 --- a/src/Runner.Worker/ExecutionContext.cs +++ b/src/Runner.Worker/ExecutionContext.cs @@ -103,7 +103,7 @@ namespace GitHub.Runner.Worker // others void ForceTaskComplete(); - void RegisterPostJobStep(string refName, IStep step); + void RegisterPostJobStep(IStep step); } public sealed class ExecutionContext : RunnerService, IExecutionContext @@ -161,6 +161,9 @@ namespace GitHub.Runner.Worker // Only job level ExecutionContext has PostJobSteps public Stack PostJobSteps { get; private set; } + // Only job level ExecutionContext has StepsWithPostRegistered + public HashSet StepsWithPostRegistered { get; private set; } + public bool EchoOnActionCommand { get; set; } @@ -248,9 +251,15 @@ namespace GitHub.Runner.Worker }); } - public void RegisterPostJobStep(string refName, IStep step) + public void RegisterPostJobStep(IStep step) { - step.ExecutionContext = Root.CreatePostChild(step.DisplayName, refName, IntraActionState); + if (step is IActionRunner actionRunner && !Root.StepsWithPostRegistered.Add(actionRunner.Action.Id)) + { + Trace.Info($"'post' of '{actionRunner.DisplayName}' already push to post step stack."); + return; + } + + step.ExecutionContext = Root.CreatePostChild(step.DisplayName, IntraActionState); Root.PostJobSteps.Push(step); } @@ -647,6 +656,9 @@ namespace GitHub.Runner.Worker // PostJobSteps for job ExecutionContext PostJobSteps = new Stack(); + // StepsWithPostRegistered for job ExecutionContext + StepsWithPostRegistered = new HashSet(); + // Job timeline record. InitializeTimelineRecord( timelineId: message.Timeline.Id, @@ -847,7 +859,7 @@ namespace GitHub.Runner.Worker } } - private IExecutionContext CreatePostChild(string displayName, string refName, Dictionary intraActionState) + private IExecutionContext CreatePostChild(string displayName, Dictionary intraActionState) { if (!_expandedForPostJob) { @@ -856,7 +868,8 @@ namespace GitHub.Runner.Worker _childTimelineRecordOrder = _childTimelineRecordOrder * 2; } - return CreateChild(Guid.NewGuid(), displayName, refName, null, null, intraActionState, _childTimelineRecordOrder - Root.PostJobSteps.Count); + var newGuid = Guid.NewGuid(); + return CreateChild(newGuid, displayName, newGuid.ToString("N"), null, null, intraActionState, _childTimelineRecordOrder - Root.PostJobSteps.Count); } } diff --git a/src/Runner.Worker/Handlers/ContainerActionHandler.cs b/src/Runner.Worker/Handlers/ContainerActionHandler.cs index 8c4f22602..10059ec68 100644 --- a/src/Runner.Worker/Handlers/ContainerActionHandler.cs +++ b/src/Runner.Worker/Handlers/ContainerActionHandler.cs @@ -82,9 +82,13 @@ namespace GitHub.Runner.Worker.Handlers container.ContainerEntryPoint = Inputs.GetValueOrDefault("entryPoint"); } } + else if (stage == ActionRunStage.Pre) + { + container.ContainerEntryPoint = Data.Pre; + } else if (stage == ActionRunStage.Post) { - container.ContainerEntryPoint = Data.Cleanup; + container.ContainerEntryPoint = Data.Post; } // create inputs context for template evaluation diff --git a/src/Runner.Worker/Handlers/NodeScriptActionHandler.cs b/src/Runner.Worker/Handlers/NodeScriptActionHandler.cs index fb3b15448..c28f3de93 100644 --- a/src/Runner.Worker/Handlers/NodeScriptActionHandler.cs +++ b/src/Runner.Worker/Handlers/NodeScriptActionHandler.cs @@ -60,9 +60,13 @@ namespace GitHub.Runner.Worker.Handlers { target = Data.Script; } + else if (stage == ActionRunStage.Pre) + { + target = Data.Pre; + } else if (stage == ActionRunStage.Post) { - target = Data.Cleanup; + target = Data.Post; } ArgUtil.NotNullOrEmpty(target, nameof(target)); diff --git a/src/Runner.Worker/Handlers/RunnerPluginHandler.cs b/src/Runner.Worker/Handlers/RunnerPluginHandler.cs index c082fe9fc..6b73b19f1 100644 --- a/src/Runner.Worker/Handlers/RunnerPluginHandler.cs +++ b/src/Runner.Worker/Handlers/RunnerPluginHandler.cs @@ -31,7 +31,7 @@ namespace GitHub.Runner.Worker.Handlers } else if (stage == ActionRunStage.Post) { - plugin = Data.Cleanup; + plugin = Data.Post; } ArgUtil.NotNullOrEmpty(plugin, nameof(plugin)); diff --git a/src/Runner.Worker/JobExtension.cs b/src/Runner.Worker/JobExtension.cs index 232ec18cf..2554039b4 100644 --- a/src/Runner.Worker/JobExtension.cs +++ b/src/Runner.Worker/JobExtension.cs @@ -197,8 +197,8 @@ namespace GitHub.Runner.Worker // Download actions not already in the cache Trace.Info("Downloading actions"); var actionManager = HostContext.GetService(); - var prepareSteps = await actionManager.PrepareActionsAsync(context, message.Steps); - preJobSteps.AddRange(prepareSteps); + var prepareResult = await actionManager.PrepareActionsAsync(context, message.Steps); + preJobSteps.AddRange(prepareResult.ContainerSetupSteps); // Add start-container steps, record and stop-container steps if (jobContext.Container != null || jobContext.ServiceContainers.Count > 0) @@ -239,9 +239,23 @@ namespace GitHub.Runner.Worker 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>(); + foreach (var preStep in prepareResult.PreStepTracker) + { + intraActionStates[preStep.Key] = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + // Create execution context for pre-job steps foreach (var step in preJobSteps) { @@ -252,6 +266,12 @@ namespace GitHub.Runner.Worker 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 @@ -260,7 +280,8 @@ namespace GitHub.Runner.Worker 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); + intraActionStates.TryGetValue(actionStep.Action.Id, out var intraActionState); + actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, actionStep.Action.ScopeName, actionStep.Action.ContextName, intraActionState); } } diff --git a/src/Runner.Worker/action_yaml.json b/src/Runner.Worker/action_yaml.json index 10e691694..7a8b847d3 100644 --- a/src/Runner.Worker/action_yaml.json +++ b/src/Runner.Worker/action_yaml.json @@ -43,6 +43,8 @@ "entrypoint": "non-empty-string", "args": "container-runs-args", "env": "container-runs-env", + "pre-entrypoint": "non-empty-string", + "pre-if": "non-empty-string", "post-entrypoint": "non-empty-string", "post-if": "non-empty-string" } @@ -67,6 +69,8 @@ "properties": { "using": "non-empty-string", "main": "non-empty-string", + "pre": "non-empty-string", + "pre-if": "non-empty-string", "post": "non-empty-string", "post-if": "non-empty-string" } diff --git a/src/Test/L0/Listener/MessageListenerL0.cs b/src/Test/L0/Listener/MessageListenerL0.cs index ea358e73e..ba0f0ee77 100644 --- a/src/Test/L0/Listener/MessageListenerL0.cs +++ b/src/Test/L0/Listener/MessageListenerL0.cs @@ -660,7 +660,7 @@ namespace GitHub.Runner.Common.Tests.Listener _settings.AgentId)) .Returns(async () => { - await Task.Delay(10); + await Task.Delay(100); return "https://t.server"; }); diff --git a/src/Test/L0/Worker/ActionManagerL0.cs b/src/Test/L0/Worker/ActionManagerL0.cs index 34b64880d..59d7ed17c 100644 --- a/src/Test/L0/Worker/ActionManagerL0.cs +++ b/src/Test/L0/Worker/ActionManagerL0.cs @@ -56,7 +56,7 @@ namespace GitHub.Runner.Common.Tests.Worker }; //Act - var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; //Assert Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]); @@ -217,7 +217,7 @@ runs: }; //Act - var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; Assert.True(steps.Count == 0); } @@ -256,7 +256,7 @@ runs: var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfile"); //Act - var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]); Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); Assert.Equal(Path.Combine(actionDir, "Dockerfile"), (steps[0].Data as ContainerSetupInfo).Container.Dockerfile); @@ -296,7 +296,7 @@ runs: var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfileinrelativepath"); //Act - var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]); Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); @@ -335,7 +335,7 @@ runs: var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfileinrelativepath"); //Act - var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]); Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); @@ -375,7 +375,7 @@ runs: var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionfile_DockerfileRelativePath"); //Act - var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]); Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); @@ -415,7 +415,7 @@ runs: var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionfile_DockerHubImage"); //Act - var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]); Assert.Equal("ubuntu:18.04", (steps[0].Data as ContainerSetupInfo).Container.Image); @@ -454,7 +454,7 @@ runs: var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionYamlFile_DockerHubImage"); //Act - var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; Assert.Equal((steps[0].Data as ContainerSetupInfo).StepIds[0], actionId); Assert.Equal("ubuntu:18.04", (steps[0].Data as ContainerSetupInfo).Container.Image); @@ -493,7 +493,7 @@ runs: var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithactionfileanddockerfile"); //Act - var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]); Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); @@ -610,7 +610,7 @@ runs: }; //Act - var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; //Assert Assert.Equal(actionId1, (steps[0].Data as ContainerSetupInfo).StepIds[0]); @@ -671,7 +671,7 @@ runs: }; //Act - var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps; // node.js based action doesn't need any extra steps to build/pull containers. Assert.True(steps.Count == 0); @@ -682,6 +682,104 @@ runs: } } + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public async void PrepareActions_RepositoryActionWithInvalidWrapperActionfile_Node() + { + try + { + //Arrange + Setup(); + var actionId = Guid.NewGuid(); + var actions = new List + { + new Pipelines.ActionStep() + { + Name = "action", + Id = actionId, + Reference = new Pipelines.RepositoryPathReference() + { + Name = "TingluoHuang/runner_L0", + Ref = "RepositoryActionWithInvalidWrapperActionfile_Node", + RepositoryType = "GitHub" + } + } + }; + + //Act + try + { + await _actionManager.PrepareActionsAsync(_ec.Object, actions); + } + catch (ArgumentException) + { + var traceFile = Path.GetTempFileName(); + File.Copy(_hc.TraceFileName, traceFile, true); + Assert.Contains("Entry javascript file is not provided.", File.ReadAllText(traceFile)); + } + } + finally + { + Teardown(); + } + } + + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public async void PrepareActions_RepositoryActionWithWrapperActionfile_PreSteps() + { + try + { + //Arrange + Setup(); + + _hc.EnqueueInstance(new Mock().Object); + _hc.EnqueueInstance(new Mock().Object); + + var actionId1 = Guid.NewGuid(); + var actionId2 = Guid.NewGuid(); + _hc.GetTrace().Info(actionId1); + _hc.GetTrace().Info(actionId2); + var actions = new List + { + new Pipelines.ActionStep() + { + Name = "action1", + Id = actionId1, + Reference = new Pipelines.RepositoryPathReference() + { + Name = "TingluoHuang/runner_L0", + Ref = "RepositoryActionWithWrapperActionfile_Node", + RepositoryType = "GitHub" + } + }, + new Pipelines.ActionStep() + { + Name = "action2", + Id = actionId2, + Reference = new Pipelines.RepositoryPathReference() + { + Name = "TingluoHuang/runner_L0", + Ref = "RepositoryActionWithWrapperActionfile_Docker", + RepositoryType = "GitHub" + } + } + }; + + //Act + var preResult = await _actionManager.PrepareActionsAsync(_ec.Object, actions); + Assert.Equal(2, preResult.PreStepTracker.Count); + Assert.NotNull(preResult.PreStepTracker[actionId1]); + Assert.NotNull(preResult.PreStepTracker[actionId2]); + } + finally + { + Teardown(); + } + } + [Fact] [Trait("Level", "L0")] [Trait("Category", "Worker")] @@ -1426,7 +1524,7 @@ runs: Assert.NotNull((definition.Data.Execution as NodeJSActionExecutionData)); Assert.Equal("task.js", (definition.Data.Execution as NodeJSActionExecutionData).Script); - Assert.Equal("cleanup.js", (definition.Data.Execution as NodeJSActionExecutionData).Cleanup); + Assert.Equal("cleanup.js", (definition.Data.Execution as NodeJSActionExecutionData).Post); } finally { @@ -1506,7 +1604,7 @@ runs: Assert.NotNull((definition.Data.Execution as ContainerActionExecutionData)); // execution.Node Assert.Equal("image:1234", (definition.Data.Execution as ContainerActionExecutionData).Image); Assert.Equal("main.sh", (definition.Data.Execution as ContainerActionExecutionData).EntryPoint); - Assert.Equal("cleanup.sh", (definition.Data.Execution as ContainerActionExecutionData).Cleanup); + Assert.Equal("cleanup.sh", (definition.Data.Execution as ContainerActionExecutionData).Post); foreach (var arg in (definition.Data.Execution as ContainerActionExecutionData).Arguments) { @@ -1595,7 +1693,7 @@ runs: Assert.NotNull((definition.Data.Execution as PluginActionExecutionData)); Assert.Equal("plugin.class, plugin", (definition.Data.Execution as PluginActionExecutionData).Plugin); - Assert.Equal("plugin.cleanup, plugin", (definition.Data.Execution as PluginActionExecutionData).Cleanup); + Assert.Equal("plugin.cleanup, plugin", (definition.Data.Execution as PluginActionExecutionData).Post); } finally { diff --git a/src/Test/L0/Worker/ActionManifestManagerL0.cs b/src/Test/L0/Worker/ActionManifestManagerL0.cs index ca789b7f3..07f99a0ae 100644 --- a/src/Test/L0/Worker/ActionManifestManagerL0.cs +++ b/src/Test/L0/Worker/ActionManifestManagerL0.cs @@ -65,6 +65,52 @@ namespace GitHub.Runner.Common.Tests.Worker } } + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public void Load_ContainerAction_Dockerfile_Pre() + { + try + { + //Arrange + Setup(); + + var actionManifest = new ActionManifestManager(); + actionManifest.Initialize(_hc); + + //Act + var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "dockerfileaction_init.yml")); + + //Assert + + Assert.Equal("Hello World", result.Name); + Assert.Equal("Greet the world and record the time", result.Description); + Assert.Equal(2, result.Inputs.Count); + Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value); + Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value); + Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value); + Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value); + + Assert.Equal(ActionExecutionType.Container, result.Execution.ExecutionType); + + var containerAction = result.Execution as ContainerActionExecutionData; + + Assert.Equal("Dockerfile", containerAction.Image); + Assert.Equal("main.sh", containerAction.EntryPoint); + Assert.Equal("init.sh", containerAction.Pre); + Assert.Equal("success()", containerAction.InitCondition); + Assert.Equal("bzz", containerAction.Arguments[0].ToString()); + Assert.Equal("Token", containerAction.Environment[0].Key.ToString()); + Assert.Equal("foo", containerAction.Environment[0].Value.ToString()); + Assert.Equal("Url", containerAction.Environment[1].Key.ToString()); + Assert.Equal("bar", containerAction.Environment[1].Value.ToString()); + } + finally + { + Teardown(); + } + } + [Fact] [Trait("Level", "L0")] [Trait("Category", "Worker")] @@ -97,7 +143,7 @@ namespace GitHub.Runner.Common.Tests.Worker Assert.Equal("Dockerfile", containerAction.Image); Assert.Equal("main.sh", containerAction.EntryPoint); - Assert.Equal("cleanup.sh", containerAction.Cleanup); + Assert.Equal("cleanup.sh", containerAction.Post); Assert.Equal("failure()", containerAction.CleanupCondition); Assert.Equal("bzz", containerAction.Arguments[0].ToString()); Assert.Equal("Token", containerAction.Environment[0].Key.ToString()); @@ -111,6 +157,52 @@ namespace GitHub.Runner.Common.Tests.Worker } } + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public void Load_ContainerAction_Dockerfile_Pre_DefaultCondition() + { + try + { + //Arrange + Setup(); + + var actionManifest = new ActionManifestManager(); + actionManifest.Initialize(_hc); + + //Act + var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "dockerfileaction_init_default.yml")); + + //Assert + + Assert.Equal("Hello World", result.Name); + Assert.Equal("Greet the world and record the time", result.Description); + Assert.Equal(2, result.Inputs.Count); + Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value); + Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value); + Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value); + Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value); + + Assert.Equal(ActionExecutionType.Container, result.Execution.ExecutionType); + + var containerAction = result.Execution as ContainerActionExecutionData; + + Assert.Equal("Dockerfile", containerAction.Image); + Assert.Equal("main.sh", containerAction.EntryPoint); + Assert.Equal("init.sh", containerAction.Pre); + Assert.Equal("always()", containerAction.InitCondition); + Assert.Equal("bzz", containerAction.Arguments[0].ToString()); + Assert.Equal("Token", containerAction.Environment[0].Key.ToString()); + Assert.Equal("foo", containerAction.Environment[0].Value.ToString()); + Assert.Equal("Url", containerAction.Environment[1].Key.ToString()); + Assert.Equal("bar", containerAction.Environment[1].Value.ToString()); + } + finally + { + Teardown(); + } + } + [Fact] [Trait("Level", "L0")] [Trait("Category", "Worker")] @@ -143,7 +235,7 @@ namespace GitHub.Runner.Common.Tests.Worker Assert.Equal("Dockerfile", containerAction.Image); Assert.Equal("main.sh", containerAction.EntryPoint); - Assert.Equal("cleanup.sh", containerAction.Cleanup); + Assert.Equal("cleanup.sh", containerAction.Post); Assert.Equal("always()", containerAction.CleanupCondition); Assert.Equal("bzz", containerAction.Arguments[0].ToString()); Assert.Equal("Token", containerAction.Environment[0].Key.ToString()); @@ -323,6 +415,94 @@ namespace GitHub.Runner.Common.Tests.Worker } } + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public void Load_NodeAction_Pre() + { + try + { + //Arrange + Setup(); + + var actionManifest = new ActionManifestManager(); + actionManifest.Initialize(_hc); + + //Act + var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "nodeaction_init.yml")); + + //Assert + Assert.Equal("Hello World", result.Name); + Assert.Equal("Greet the world and record the time", result.Description); + Assert.Equal(2, result.Inputs.Count); + Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value); + Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value); + Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value); + Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value); + Assert.Equal(1, result.Deprecated.Count); + + Assert.True(result.Deprecated.ContainsKey("greeting")); + result.Deprecated.TryGetValue("greeting", out string value); + Assert.Equal("This property has been deprecated", value); + + Assert.Equal(ActionExecutionType.NodeJS, result.Execution.ExecutionType); + + var nodeAction = result.Execution as NodeJSActionExecutionData; + + Assert.Equal("main.js", nodeAction.Script); + Assert.Equal("init.js", nodeAction.Pre); + Assert.Equal("cancelled()", nodeAction.InitCondition); + } + finally + { + Teardown(); + } + } + + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public void Load_NodeAction_Init_DefaultCondition() + { + try + { + //Arrange + Setup(); + + var actionManifest = new ActionManifestManager(); + actionManifest.Initialize(_hc); + + //Act + var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "nodeaction_init_default.yml")); + + //Assert + Assert.Equal("Hello World", result.Name); + Assert.Equal("Greet the world and record the time", result.Description); + Assert.Equal(2, result.Inputs.Count); + Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value); + Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value); + Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value); + Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value); + Assert.Equal(1, result.Deprecated.Count); + + Assert.True(result.Deprecated.ContainsKey("greeting")); + result.Deprecated.TryGetValue("greeting", out string value); + Assert.Equal("This property has been deprecated", value); + + Assert.Equal(ActionExecutionType.NodeJS, result.Execution.ExecutionType); + + var nodeAction = result.Execution as NodeJSActionExecutionData; + + Assert.Equal("main.js", nodeAction.Script); + Assert.Equal("init.js", nodeAction.Pre); + Assert.Equal("always()", nodeAction.InitCondition); + } + finally + { + Teardown(); + } + } + [Fact] [Trait("Level", "L0")] [Trait("Category", "Worker")] @@ -358,7 +538,7 @@ namespace GitHub.Runner.Common.Tests.Worker var nodeAction = result.Execution as NodeJSActionExecutionData; Assert.Equal("main.js", nodeAction.Script); - Assert.Equal("cleanup.js", nodeAction.Cleanup); + Assert.Equal("cleanup.js", nodeAction.Post); Assert.Equal("cancelled()", nodeAction.CleanupCondition); } finally @@ -402,7 +582,7 @@ namespace GitHub.Runner.Common.Tests.Worker var nodeAction = result.Execution as NodeJSActionExecutionData; Assert.Equal("main.js", nodeAction.Script); - Assert.Equal("cleanup.js", nodeAction.Cleanup); + Assert.Equal("cleanup.js", nodeAction.Post); Assert.Equal("always()", nodeAction.CleanupCondition); } finally diff --git a/src/Test/L0/Worker/ExecutionContextL0.cs b/src/Test/L0/Worker/ExecutionContextL0.cs index aef52d402..38fe135ee 100644 --- a/src/Test/L0/Worker/ExecutionContextL0.cs +++ b/src/Test/L0/Worker/ExecutionContextL0.cs @@ -199,20 +199,20 @@ namespace GitHub.Runner.Common.Tests.Worker var postRunner1 = hc.CreateService(); - postRunner1.Action = new Pipelines.ActionStep() { Name = "post1", DisplayName = "Test 1", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } }; + postRunner1.Action = new Pipelines.ActionStep() { Id = Guid.NewGuid(), Name = "post1", DisplayName = "Test 1", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } }; postRunner1.Stage = ActionRunStage.Post; postRunner1.Condition = "always()"; postRunner1.DisplayName = "post1"; var postRunner2 = hc.CreateService(); - postRunner2.Action = new Pipelines.ActionStep() { Name = "post2", DisplayName = "Test 2", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } }; + postRunner2.Action = new Pipelines.ActionStep() { Id = Guid.NewGuid(), Name = "post2", DisplayName = "Test 2", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } }; postRunner2.Stage = ActionRunStage.Post; postRunner2.Condition = "always()"; postRunner2.DisplayName = "post2"; - action1.RegisterPostJobStep("post1", postRunner1); - action2.RegisterPostJobStep("post2", postRunner2); + action1.RegisterPostJobStep(postRunner1); + action2.RegisterPostJobStep(postRunner2); Assert.NotNull(jobContext.JobSteps); Assert.NotNull(jobContext.PostJobSteps); @@ -238,6 +238,91 @@ namespace GitHub.Runner.Common.Tests.Worker } } + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public void RegisterPostJobAction_NotRegisterPostTwice() + { + using (TestHostContext hc = CreateTestContext()) + { + // Arrange: Create a job request message. + TaskOrchestrationPlanReference plan = new TaskOrchestrationPlanReference(); + TimelineReference timeline = new TimelineReference(); + 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); + jobRequest.Resources.Repositories.Add(new Pipelines.RepositoryResource() + { + Alias = Pipelines.PipelineConstants.SelfAlias, + Id = "github", + Version = "sha1" + }); + jobRequest.ContextData["github"] = new Pipelines.ContextData.DictionaryContextData(); + jobRequest.Variables["ACTIONS_STEP_DEBUG"] = "true"; + + // Arrange: Setup the paging logger. + var pagingLogger1 = new Mock(); + var pagingLogger2 = new Mock(); + var pagingLogger3 = new Mock(); + var pagingLogger4 = new Mock(); + var pagingLogger5 = new Mock(); + var jobServerQueue = new Mock(); + jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny(), It.IsAny())); + jobServerQueue.Setup(x => x.QueueWebConsoleLine(It.IsAny(), It.IsAny())).Callback((Guid id, string msg) => { hc.GetTrace().Info(msg); }); + + var actionRunner1 = new ActionRunner(); + actionRunner1.Initialize(hc); + var actionRunner2 = new ActionRunner(); + actionRunner2.Initialize(hc); + + hc.EnqueueInstance(pagingLogger1.Object); + hc.EnqueueInstance(pagingLogger2.Object); + hc.EnqueueInstance(pagingLogger3.Object); + hc.EnqueueInstance(pagingLogger4.Object); + hc.EnqueueInstance(pagingLogger5.Object); + hc.EnqueueInstance(actionRunner1 as IActionRunner); + hc.EnqueueInstance(actionRunner2 as IActionRunner); + hc.SetSingleton(jobServerQueue.Object); + + var jobContext = new Runner.Worker.ExecutionContext(); + jobContext.Initialize(hc); + + // Act. + jobContext.InitializeJob(jobRequest, CancellationToken.None); + + var action1 = jobContext.CreateChild(Guid.NewGuid(), "action_1_pre", "action_1_pre", null, null); + var action2 = jobContext.CreateChild(Guid.NewGuid(), "action_1_main", "action_1_main", null, null); + + var actionId = Guid.NewGuid(); + var postRunner1 = hc.CreateService(); + postRunner1.Action = new Pipelines.ActionStep() { Id = actionId, Name = "post1", DisplayName = "Test 1", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } }; + postRunner1.Stage = ActionRunStage.Post; + postRunner1.Condition = "always()"; + postRunner1.DisplayName = "post1"; + + + var postRunner2 = hc.CreateService(); + postRunner2.Action = new Pipelines.ActionStep() { Id = actionId, Name = "post2", DisplayName = "Test 2", Reference = new Pipelines.RepositoryPathReference() { Name = "actions/action" } }; + postRunner2.Stage = ActionRunStage.Post; + postRunner2.Condition = "always()"; + postRunner2.DisplayName = "post2"; + + action1.RegisterPostJobStep(postRunner1); + action2.RegisterPostJobStep(postRunner2); + + Assert.NotNull(jobContext.JobSteps); + Assert.NotNull(jobContext.PostJobSteps); + Assert.Equal(1, jobContext.PostJobSteps.Count); + var post1 = jobContext.PostJobSteps.Pop(); + + Assert.Equal("post1", (post1 as IActionRunner).Action.Name); + + Assert.Equal(ActionRunStage.Post, (post1 as IActionRunner).Stage); + + Assert.Equal("always()", (post1 as IActionRunner).Condition); + } + } + private TestHostContext CreateTestContext([CallerMemberName] String testName = "") { var hc = new TestHostContext(this, testName); diff --git a/src/Test/L0/Worker/JobExtensionL0.cs b/src/Test/L0/Worker/JobExtensionL0.cs index a8c4573e6..0101db135 100644 --- a/src/Test/L0/Worker/JobExtensionL0.cs +++ b/src/Test/L0/Worker/JobExtensionL0.cs @@ -141,7 +141,7 @@ namespace GitHub.Runner.Common.Tests.Worker jobExtension.Initialize(hc); _actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny(), It.IsAny>())) - .Returns(Task.FromResult(new List())); + .Returns(Task.FromResult(new PrepareResult(new List(), new Dictionary()))); List result = await jobExtension.InitializeJob(_jobEc, _message); @@ -176,7 +176,7 @@ namespace GitHub.Runner.Common.Tests.Worker jobExtension.Initialize(hc); _actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny(), It.IsAny>())) - .Returns(Task.FromResult(new List() { new JobExtensionRunner(null, "", "prepare1", null), new JobExtensionRunner(null, "", "prepare2", null) })); + .Returns(Task.FromResult(new PrepareResult(new List() { new JobExtensionRunner(null, "", "prepare1", null), new JobExtensionRunner(null, "", "prepare2", null) }, new Dictionary()))); List result = await jobExtension.InitializeJob(_jobEc, _message); diff --git a/src/Test/TestData/dockerfileaction_init.yml b/src/Test/TestData/dockerfileaction_init.yml new file mode 100644 index 000000000..3407f58a9 --- /dev/null +++ b/src/Test/TestData/dockerfileaction_init.yml @@ -0,0 +1,27 @@ +name: 'Hello World' +description: 'Greet the world and record the time' +author: 'Test Corporation' +inputs: + greeting: # id of input + description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout' + required: true + default: 'Hello' + entryPoint: # id of input + description: 'optional docker entrypoint overwrite.' + required: false +outputs: + time: # id of output + description: 'The time we did the greeting' +icon: 'hello.svg' # vector art to display in the GitHub Marketplace +color: 'green' # optional, decorates the entry in the GitHub Marketplace +runs: + using: 'docker' + image: 'Dockerfile' + args: + - 'bzz' + entrypoint: 'main.sh' + env: + Token: foo + Url: bar + pre-entrypoint: 'init.sh' + pre-if: 'success()' \ No newline at end of file diff --git a/src/Test/TestData/dockerfileaction_init_default.yml b/src/Test/TestData/dockerfileaction_init_default.yml new file mode 100644 index 000000000..923fb8beb --- /dev/null +++ b/src/Test/TestData/dockerfileaction_init_default.yml @@ -0,0 +1,26 @@ +name: 'Hello World' +description: 'Greet the world and record the time' +author: 'Test Corporation' +inputs: + greeting: # id of input + description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout' + required: true + default: 'Hello' + entryPoint: # id of input + description: 'optional docker entrypoint overwrite.' + required: false +outputs: + time: # id of output + description: 'The time we did the greeting' +icon: 'hello.svg' # vector art to display in the GitHub Marketplace +color: 'green' # optional, decorates the entry in the GitHub Marketplace +runs: + using: 'docker' + image: 'Dockerfile' + args: + - 'bzz' + entrypoint: 'main.sh' + env: + Token: foo + Url: bar + pre-entrypoint: 'init.sh' \ No newline at end of file diff --git a/src/Test/TestData/nodeaction_init.yml b/src/Test/TestData/nodeaction_init.yml new file mode 100644 index 000000000..c1140b328 --- /dev/null +++ b/src/Test/TestData/nodeaction_init.yml @@ -0,0 +1,22 @@ +name: 'Hello World' +description: 'Greet the world and record the time' +author: 'Test Corporation' +inputs: + greeting: # id of input + description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout' + required: true + default: 'Hello' + deprecationMessage: 'This property has been deprecated' + entryPoint: # id of input + description: 'optional docker entrypoint overwrite.' + required: false +outputs: + time: # id of output + description: 'The time we did the greeting' +icon: 'hello.svg' # vector art to display in the GitHub Marketplace +color: 'green' # optional, decorates the entry in the GitHub Marketplace +runs: + using: 'node12' + main: 'main.js' + pre: 'init.js' + pre-if: 'cancelled()' \ No newline at end of file diff --git a/src/Test/TestData/nodeaction_init_default.yml b/src/Test/TestData/nodeaction_init_default.yml new file mode 100644 index 000000000..8d300a11a --- /dev/null +++ b/src/Test/TestData/nodeaction_init_default.yml @@ -0,0 +1,21 @@ +name: 'Hello World' +description: 'Greet the world and record the time' +author: 'Test Corporation' +inputs: + greeting: # id of input + description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout' + required: true + default: 'Hello' + deprecationMessage: 'This property has been deprecated' + entryPoint: # id of input + description: 'optional docker entrypoint overwrite.' + required: false +outputs: + time: # id of output + description: 'The time we did the greeting' +icon: 'hello.svg' # vector art to display in the GitHub Marketplace +color: 'green' # optional, decorates the entry in the GitHub Marketplace +runs: + using: 'node12' + main: 'main.js' + pre: 'init.js' \ No newline at end of file