diff --git a/src/Runner.Worker/ActionManager.cs b/src/Runner.Worker/ActionManager.cs index 28ab95591..290c3518f 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: Depreciate the PREVIEW_ACTION_TOKEN @@ -59,7 +73,7 @@ namespace GitHub.Runner.Worker } // Clear the cache (local runner) - IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken); + // IOUtil.DeleteDirectory(HostContext.GetDirectory(WellKnownDirectory.Actions), executionContext.CancellationToken); foreach (var action in actions) { @@ -111,6 +125,20 @@ namespace GitHub.Runner.Worker imagesToBuildInfo[setupInfo.ActionRepository] = setupInfo; } } + + if (!(action.Reference is Pipelines.RepositoryPathReference repoAction) || repoAction.RepositoryType != Pipelines.PipelineConstants.SelfAlias) + { + var definition = LoadAction(executionContext, action); + if (definition.Data.Execution.HasInit) + { + var actionRunner = HostContext.CreateService(); + actionRunner.Action = action; + actionRunner.Stage = ActionRunStage.Pre; + actionRunner.Condition = definition.Data.Execution.InitCondition; + + preStepTracker[action.Id] = actionRunner; + } + } } } @@ -147,7 +175,7 @@ namespace GitHub.Runner.Worker } #endif - return containerSetupSteps; + return new PrepareResult(containerSetupSteps, preStepTracker); } public Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action) @@ -772,6 +800,8 @@ namespace GitHub.Runner.Worker { public override ActionExecutionType ExecutionType => ActionExecutionType.Container; + public override bool HasInit => !string.IsNullOrEmpty(Init); + public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup); public string Image { get; set; } @@ -782,6 +812,8 @@ namespace GitHub.Runner.Worker public MappingToken Environment { get; set; } + public string Init { get; set; } + public string Cleanup { get; set; } } @@ -789,10 +821,14 @@ namespace GitHub.Runner.Worker { public override ActionExecutionType ExecutionType => ActionExecutionType.NodeJS; + public override bool HasInit => !string.IsNullOrEmpty(Init); + public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup); public string Script { get; set; } + public string Init { get; set; } + public string Cleanup { get; set; } } @@ -800,6 +836,8 @@ namespace GitHub.Runner.Worker { public override ActionExecutionType ExecutionType => ActionExecutionType.Plugin; + public override bool HasInit => false; + public override bool HasCleanup => !string.IsNullOrEmpty(Cleanup); public string Plugin { get; set; } @@ -810,16 +848,18 @@ namespace GitHub.Runner.Worker public sealed class ScriptActionExecutionData : ActionExecutionData { public override ActionExecutionType ExecutionType => ActionExecutionType.Script; - + public override bool HasInit => false; public override bool HasCleanup => 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 HasInit { get; } public abstract bool HasCleanup { get; } public string CleanupCondition @@ -827,6 +867,12 @@ namespace GitHub.Runner.Worker 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 82e4c2ae9..040d45fbe 100644 --- a/src/Runner.Worker/ActionManifestManager.cs +++ b/src/Runner.Worker/ActionManifestManager.cs @@ -280,6 +280,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); @@ -318,6 +321,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; @@ -340,6 +352,8 @@ namespace GitHub.Runner.Worker Arguments = argsToken, EntryPoint = entrypointToken?.Value, Environment = envToken, + Init = preEntrypointToken?.Value, + InitCondition = preIfToken?.Value ?? "always()", Cleanup = postEntrypointToken?.Value, CleanupCondition = postIfToken?.Value ?? "always()" }; @@ -356,6 +370,8 @@ namespace GitHub.Runner.Worker return new NodeJSActionExecutionData() { Script = mainToken.Value, + Init = preToken?.Value, + InitCondition = preIfToken?.Value ?? "always()", Cleanup = postToken?.Value, CleanupCondition = postIfToken?.Value ?? "always()" }; diff --git a/src/Runner.Worker/ActionRunner.cs b/src/Runner.Worker/ActionRunner.cs index c604679bc..46a3120f7 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, } diff --git a/src/Runner.Worker/Handlers/ContainerActionHandler.cs b/src/Runner.Worker/Handlers/ContainerActionHandler.cs index a623da968..054461ed0 100644 --- a/src/Runner.Worker/Handlers/ContainerActionHandler.cs +++ b/src/Runner.Worker/Handlers/ContainerActionHandler.cs @@ -82,6 +82,10 @@ namespace GitHub.Runner.Worker.Handlers container.ContainerEntryPoint = Inputs.GetValueOrDefault("entryPoint"); } } + else if (stage == ActionRunStage.Pre) + { + container.ContainerEntryPoint = Data.Init; + } else if (stage == ActionRunStage.Post) { container.ContainerEntryPoint = Data.Cleanup; diff --git a/src/Runner.Worker/Handlers/NodeScriptActionHandler.cs b/src/Runner.Worker/Handlers/NodeScriptActionHandler.cs index fb3b15448..efefe9f00 100644 --- a/src/Runner.Worker/Handlers/NodeScriptActionHandler.cs +++ b/src/Runner.Worker/Handlers/NodeScriptActionHandler.cs @@ -60,6 +60,10 @@ namespace GitHub.Runner.Worker.Handlers { target = Data.Script; } + else if (stage == ActionRunStage.Pre) + { + target = Data.Init; + } else if (stage == ActionRunStage.Post) { target = Data.Cleanup; diff --git a/src/Runner.Worker/JobExtension.cs b/src/Runner.Worker/JobExtension.cs index 11834b8f3..5a176ded9 100644 --- a/src/Runner.Worker/JobExtension.cs +++ b/src/Runner.Worker/JobExtension.cs @@ -116,8 +116,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) @@ -158,9 +158,31 @@ 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); + if (preStep.DisplayName.StartsWith(PipelineTemplateConstants.RunDisplayPrefix)) + { + preStep.DisplayName = $"Pre {preStep.DisplayName.Substring(PipelineTemplateConstants.RunDisplayPrefix.Length)}"; + } + else + { + preStep.DisplayName = $"Pre {preStep.DisplayName}"; + } + + preJobSteps.Add(preStep); + } } } + var intraActionStates = new Dictionary>(); + foreach (var preStep in prepareResult.PreStepTracker) + { + intraActionStates[preStep.Key] = new Dictionary(); + } + // Create execution context for pre-job steps foreach (var step in preJobSteps) { @@ -171,6 +193,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, null, null, stepId.ToString("N"), intraActionStates[actionStep.Action.Id]); + } } // Create execution context for job steps @@ -179,7 +207,14 @@ 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); + if (intraActionStates.ContainsKey(actionStep.Action.Id)) + { + actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, actionStep.Action.ScopeName, actionStep.Action.ContextName, intraActionStates[actionStep.Action.Id]); + } + else + { + actionStep.ExecutionContext = jobContext.CreateChild(actionStep.Action.Id, actionStep.DisplayName, actionStep.Action.Name, actionStep.Action.ScopeName, actionStep.Action.ContextName); + } } } diff --git a/src/Runner.Worker/action_yaml.json b/src/Runner.Worker/action_yaml.json index a30de1606..832f64beb 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/Worker/ActionManagerL0.cs b/src/Test/L0/Worker/ActionManagerL0.cs index 51e09bff1..317259e22 100644 --- a/src/Test/L0/Worker/ActionManagerL0.cs +++ b/src/Test/L0/Worker/ActionManagerL0.cs @@ -54,7 +54,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]); @@ -164,7 +164,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.True(steps.Count == 0); } @@ -203,7 +203,7 @@ namespace GitHub.Runner.Common.Tests.Worker 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); @@ -243,7 +243,7 @@ namespace GitHub.Runner.Common.Tests.Worker 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); @@ -282,7 +282,7 @@ namespace GitHub.Runner.Common.Tests.Worker 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); @@ -322,7 +322,7 @@ namespace GitHub.Runner.Common.Tests.Worker 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); @@ -362,7 +362,7 @@ namespace GitHub.Runner.Common.Tests.Worker 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); @@ -401,7 +401,7 @@ namespace GitHub.Runner.Common.Tests.Worker 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); @@ -440,7 +440,7 @@ namespace GitHub.Runner.Common.Tests.Worker 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); @@ -557,7 +557,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(actionId1, (steps[0].Data as ContainerSetupInfo).StepIds[0]); @@ -618,7 +618,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; // node.js based action doesn't need any extra steps to build/pull containers. Assert.True(steps.Count == 0); diff --git a/src/Test/L0/Worker/ActionManifestManagerL0.cs b/src/Test/L0/Worker/ActionManifestManagerL0.cs index 757610706..0eeeda12e 100644 --- a/src/Test/L0/Worker/ActionManifestManagerL0.cs +++ b/src/Test/L0/Worker/ActionManifestManagerL0.cs @@ -63,6 +63,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.Init); + 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")] @@ -109,6 +155,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.Init); + 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")] @@ -321,6 +413,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.Init); + 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.Init); + Assert.Equal("always()", nodeAction.InitCondition); + } + finally + { + Teardown(); + } + } + [Fact] [Trait("Level", "L0")] [Trait("Category", "Worker")] diff --git a/src/Test/L0/Worker/JobExtensionL0.cs b/src/Test/L0/Worker/JobExtensionL0.cs index 447a760dc..5fd71ef45 100644 --- a/src/Test/L0/Worker/JobExtensionL0.cs +++ b/src/Test/L0/Worker/JobExtensionL0.cs @@ -144,7 +144,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); @@ -179,7 +179,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