support 'pre' execution for actions. (#389)

This commit is contained in:
Tingluo Huang
2020-04-13 21:46:30 -04:00
committed by GitHub
parent f9baec4b32
commit d5c7097d2c
19 changed files with 640 additions and 68 deletions

View File

@@ -21,11 +21,24 @@ using PipelineTemplateConstants = GitHub.DistributedTask.Pipelines.ObjectTemplat
namespace GitHub.Runner.Worker namespace GitHub.Runner.Worker
{ {
public class PrepareResult
{
public PrepareResult(List<JobExtensionRunner> containerSetupSteps, Dictionary<Guid, IActionRunner> preStepTracker)
{
this.ContainerSetupSteps = containerSetupSteps;
this.PreStepTracker = preStepTracker;
}
public List<JobExtensionRunner> ContainerSetupSteps { get; set; }
public Dictionary<Guid, IActionRunner> PreStepTracker { get; set; }
}
[ServiceLocator(Default = typeof(ActionManager))] [ServiceLocator(Default = typeof(ActionManager))]
public interface IActionManager : IRunnerService public interface IActionManager : IRunnerService
{ {
Dictionary<Guid, ContainerInfo> CachedActionContainers { get; } Dictionary<Guid, ContainerInfo> CachedActionContainers { get; }
Task<List<JobExtensionRunner>> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps); Task<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps);
Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action); Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action);
} }
@@ -39,7 +52,7 @@ namespace GitHub.Runner.Worker
private readonly Dictionary<Guid, ContainerInfo> _cachedActionContainers = new Dictionary<Guid, ContainerInfo>(); private readonly Dictionary<Guid, ContainerInfo> _cachedActionContainers = new Dictionary<Guid, ContainerInfo>();
public Dictionary<Guid, ContainerInfo> CachedActionContainers => _cachedActionContainers; public Dictionary<Guid, ContainerInfo> CachedActionContainers => _cachedActionContainers;
public async Task<List<JobExtensionRunner>> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps) public async Task<PrepareResult> PrepareActionsAsync(IExecutionContext executionContext, IEnumerable<Pipelines.JobStep> steps)
{ {
ArgUtil.NotNull(executionContext, nameof(executionContext)); ArgUtil.NotNull(executionContext, nameof(executionContext));
ArgUtil.NotNull(steps, nameof(steps)); ArgUtil.NotNull(steps, nameof(steps));
@@ -49,6 +62,7 @@ namespace GitHub.Runner.Worker
Dictionary<string, List<Guid>> imagesToBuild = new Dictionary<string, List<Guid>>(StringComparer.OrdinalIgnoreCase); Dictionary<string, List<Guid>> imagesToBuild = new Dictionary<string, List<Guid>>(StringComparer.OrdinalIgnoreCase);
Dictionary<string, ActionContainer> imagesToBuildInfo = new Dictionary<string, ActionContainer>(StringComparer.OrdinalIgnoreCase); Dictionary<string, ActionContainer> imagesToBuildInfo = new Dictionary<string, ActionContainer>(StringComparer.OrdinalIgnoreCase);
List<JobExtensionRunner> containerSetupSteps = new List<JobExtensionRunner>(); List<JobExtensionRunner> containerSetupSteps = new List<JobExtensionRunner>();
Dictionary<Guid, IActionRunner> preStepTracker = new Dictionary<Guid, IActionRunner>();
IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>(); IEnumerable<Pipelines.ActionStep> actions = steps.OfType<Pipelines.ActionStep>();
// TODO: Deprecate the PREVIEW_ACTION_TOKEN // TODO: Deprecate the PREVIEW_ACTION_TOKEN
@@ -117,6 +131,22 @@ namespace GitHub.Runner.Worker
imagesToBuildInfo[setupInfo.ActionRepository] = setupInfo; 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<IActionRunner>();
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 #endif
return containerSetupSteps; return new PrepareResult(containerSetupSteps, preStepTracker);
} }
public Definition LoadAction(IExecutionContext executionContext, Pipelines.ActionStep action) 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)}."); 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)) if (!string.IsNullOrEmpty(containerAction.EntryPoint))
{ {
Trace.Info($"Action container entrypoint: {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)) if (CachedActionContainers.TryGetValue(action.Id, out var container))
@@ -264,8 +299,9 @@ namespace GitHub.Runner.Worker
else if (definition.Data.Execution.ExecutionType == ActionExecutionType.NodeJS) else if (definition.Data.Execution.ExecutionType == ActionExecutionType.NodeJS)
{ {
var nodeAction = definition.Data.Execution as NodeJSActionExecutionData; 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 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) else if (definition.Data.Execution.ExecutionType == ActionExecutionType.Plugin)
{ {
@@ -281,7 +317,7 @@ namespace GitHub.Runner.Worker
if (!string.IsNullOrEmpty(plugin.PostPluginTypeName)) if (!string.IsNullOrEmpty(plugin.PostPluginTypeName))
{ {
pluginAction.Cleanup = plugin.PostPluginTypeName; pluginAction.Post = plugin.PostPluginTypeName;
Trace.Info($"Action cleanup plugin: {plugin.PluginTypeName}."); Trace.Info($"Action cleanup plugin: {plugin.PluginTypeName}.");
} }
} }
@@ -788,7 +824,8 @@ namespace GitHub.Runner.Worker
{ {
public override ActionExecutionType ExecutionType => ActionExecutionType.Container; 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; } public string Image { get; set; }
@@ -798,51 +835,66 @@ namespace GitHub.Runner.Worker
public MappingToken Environment { get; set; } 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 sealed class NodeJSActionExecutionData : ActionExecutionData
{ {
public override ActionExecutionType ExecutionType => ActionExecutionType.NodeJS; 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 Script { get; set; }
public string Cleanup { get; set; } public string Pre { get; set; }
public string Post { get; set; }
} }
public sealed class PluginActionExecutionData : ActionExecutionData public sealed class PluginActionExecutionData : ActionExecutionData
{ {
public override ActionExecutionType ExecutionType => ActionExecutionType.Plugin; 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 Plugin { get; set; }
public string Cleanup { get; set; } public string Post { get; set; }
} }
public sealed class ScriptActionExecutionData : ActionExecutionData public sealed class ScriptActionExecutionData : ActionExecutionData
{ {
public override ActionExecutionType ExecutionType => ActionExecutionType.Script; public override ActionExecutionType ExecutionType => ActionExecutionType.Script;
public override bool HasPre => false;
public override bool HasCleanup => false; public override bool HasPost => false;
} }
public abstract class ActionExecutionData public abstract class ActionExecutionData
{ {
private string _initCondition = $"{Constants.Expressions.Always}()";
private string _cleanupCondition = $"{Constants.Expressions.Always}()"; private string _cleanupCondition = $"{Constants.Expressions.Always}()";
public abstract ActionExecutionType ExecutionType { get; } public abstract ActionExecutionType ExecutionType { get; }
public abstract bool HasCleanup { get; } public abstract bool HasPre { get; }
public abstract bool HasPost { get; }
public string CleanupCondition public string CleanupCondition
{ {
get { return _cleanupCondition; } get { return _cleanupCondition; }
set { _cleanupCondition = value; } set { _cleanupCondition = value; }
} }
public string InitCondition
{
get { return _initCondition; }
set { _initCondition = value; }
}
} }
public class ContainerSetupInfo public class ContainerSetupInfo

View File

@@ -305,6 +305,9 @@ namespace GitHub.Runner.Worker
var envToken = default(MappingToken); var envToken = default(MappingToken);
var mainToken = default(StringToken); var mainToken = default(StringToken);
var pluginToken = default(StringToken); var pluginToken = default(StringToken);
var preToken = default(StringToken);
var preEntrypointToken = default(StringToken);
var preIfToken = default(StringToken);
var postToken = default(StringToken); var postToken = default(StringToken);
var postEntrypointToken = default(StringToken); var postEntrypointToken = default(StringToken);
var postIfToken = default(StringToken); var postIfToken = default(StringToken);
@@ -343,6 +346,15 @@ namespace GitHub.Runner.Worker
case "post-if": case "post-if":
postIfToken = run.Value.AssertString("post-if"); postIfToken = run.Value.AssertString("post-if");
break; 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: default:
Trace.Info($"Ignore run property {runsKey}."); Trace.Info($"Ignore run property {runsKey}.");
break; break;
@@ -365,7 +377,9 @@ namespace GitHub.Runner.Worker
Arguments = argsToken, Arguments = argsToken,
EntryPoint = entrypointToken?.Value, EntryPoint = entrypointToken?.Value,
Environment = envToken, Environment = envToken,
Cleanup = postEntrypointToken?.Value, Pre = preEntrypointToken?.Value,
InitCondition = preIfToken?.Value ?? "always()",
Post = postEntrypointToken?.Value,
CleanupCondition = postIfToken?.Value ?? "always()" CleanupCondition = postIfToken?.Value ?? "always()"
}; };
} }
@@ -374,14 +388,16 @@ namespace GitHub.Runner.Worker
{ {
if (string.IsNullOrEmpty(mainToken?.Value)) if (string.IsNullOrEmpty(mainToken?.Value))
{ {
throw new ArgumentNullException($"Entry javascript fils is not provided."); throw new ArgumentNullException($"Entry javascript file is not provided.");
} }
else else
{ {
return new NodeJSActionExecutionData() return new NodeJSActionExecutionData()
{ {
Script = mainToken.Value, Script = mainToken.Value,
Cleanup = postToken?.Value, Pre = preToken?.Value,
InitCondition = preIfToken?.Value ?? "always()",
Post = postToken?.Value,
CleanupCondition = postIfToken?.Value ?? "always()" CleanupCondition = postIfToken?.Value ?? "always()"
}; };
} }

View File

@@ -18,6 +18,7 @@ namespace GitHub.Runner.Worker
{ {
public enum ActionRunStage public enum ActionRunStage
{ {
Pre,
Main, Main,
Post, Post,
} }
@@ -81,20 +82,18 @@ namespace GitHub.Runner.Worker
ActionExecutionData handlerData = definition.Data?.Execution; ActionExecutionData handlerData = definition.Data?.Execution;
ArgUtil.NotNull(handlerData, nameof(handlerData)); 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. // 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 // 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; string postDisplayName = $"Post {this.DisplayName}";
if (this.DisplayName.StartsWith(PipelineTemplateConstants.RunDisplayPrefix))
{
postDisplayName = $"Post {this.DisplayName.Substring(PipelineTemplateConstants.RunDisplayPrefix.Length)}";
}
else
{
postDisplayName = $"Post {this.DisplayName}";
}
var repositoryReference = Action.Reference as RepositoryPathReference; var repositoryReference = Action.Reference as RepositoryPathReference;
var pathString = string.IsNullOrEmpty(repositoryReference.Path) ? string.Empty : $"/{repositoryReference.Path}"; var pathString = string.IsNullOrEmpty(repositoryReference.Path) ? string.Empty : $"/{repositoryReference.Path}";
var repoString = string.IsNullOrEmpty(repositoryReference.Ref) ? $"{repositoryReference.Name}{pathString}" : var repoString = string.IsNullOrEmpty(repositoryReference.Ref) ? $"{repositoryReference.Name}{pathString}" :
@@ -108,7 +107,7 @@ namespace GitHub.Runner.Worker
actionRunner.Condition = handlerData.CleanupCondition; actionRunner.Condition = handlerData.CleanupCondition;
actionRunner.DisplayName = postDisplayName; actionRunner.DisplayName = postDisplayName;
ExecutionContext.RegisterPostJobStep($"{actionRunner.Action.Name}_post", actionRunner); ExecutionContext.RegisterPostJobStep(actionRunner);
} }
IStepHost stepHost = HostContext.CreateService<IDefaultStepHost>(); IStepHost stepHost = HostContext.CreateService<IDefaultStepHost>();

View File

@@ -49,7 +49,7 @@ namespace GitHub.Runner.Worker
data: data); data: data);
executionContext.Debug($"Register post job cleanup for stopping/deleting containers."); 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. // Check whether we are inside a container.
// Our container feature requires to map working directory from host to the container. // Our container feature requires to map working directory from host to the container.

View File

@@ -103,7 +103,7 @@ namespace GitHub.Runner.Worker
// others // others
void ForceTaskComplete(); void ForceTaskComplete();
void RegisterPostJobStep(string refName, IStep step); void RegisterPostJobStep(IStep step);
} }
public sealed class ExecutionContext : RunnerService, IExecutionContext public sealed class ExecutionContext : RunnerService, IExecutionContext
@@ -161,6 +161,9 @@ namespace GitHub.Runner.Worker
// Only job level ExecutionContext has PostJobSteps // Only job level ExecutionContext has PostJobSteps
public Stack<IStep> PostJobSteps { get; private set; } public Stack<IStep> PostJobSteps { get; private set; }
// Only job level ExecutionContext has StepsWithPostRegistered
public HashSet<Guid> StepsWithPostRegistered { get; private set; }
public bool EchoOnActionCommand { get; 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); Root.PostJobSteps.Push(step);
} }
@@ -647,6 +656,9 @@ namespace GitHub.Runner.Worker
// PostJobSteps for job ExecutionContext // PostJobSteps for job ExecutionContext
PostJobSteps = new Stack<IStep>(); PostJobSteps = new Stack<IStep>();
// StepsWithPostRegistered for job ExecutionContext
StepsWithPostRegistered = new HashSet<Guid>();
// Job timeline record. // Job timeline record.
InitializeTimelineRecord( InitializeTimelineRecord(
timelineId: message.Timeline.Id, timelineId: message.Timeline.Id,
@@ -847,7 +859,7 @@ namespace GitHub.Runner.Worker
} }
} }
private IExecutionContext CreatePostChild(string displayName, string refName, Dictionary<string, string> intraActionState) private IExecutionContext CreatePostChild(string displayName, Dictionary<string, string> intraActionState)
{ {
if (!_expandedForPostJob) if (!_expandedForPostJob)
{ {
@@ -856,7 +868,8 @@ namespace GitHub.Runner.Worker
_childTimelineRecordOrder = _childTimelineRecordOrder * 2; _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);
} }
} }

View File

@@ -82,9 +82,13 @@ namespace GitHub.Runner.Worker.Handlers
container.ContainerEntryPoint = Inputs.GetValueOrDefault("entryPoint"); container.ContainerEntryPoint = Inputs.GetValueOrDefault("entryPoint");
} }
} }
else if (stage == ActionRunStage.Pre)
{
container.ContainerEntryPoint = Data.Pre;
}
else if (stage == ActionRunStage.Post) else if (stage == ActionRunStage.Post)
{ {
container.ContainerEntryPoint = Data.Cleanup; container.ContainerEntryPoint = Data.Post;
} }
// create inputs context for template evaluation // create inputs context for template evaluation

View File

@@ -60,9 +60,13 @@ namespace GitHub.Runner.Worker.Handlers
{ {
target = Data.Script; target = Data.Script;
} }
else if (stage == ActionRunStage.Pre)
{
target = Data.Pre;
}
else if (stage == ActionRunStage.Post) else if (stage == ActionRunStage.Post)
{ {
target = Data.Cleanup; target = Data.Post;
} }
ArgUtil.NotNullOrEmpty(target, nameof(target)); ArgUtil.NotNullOrEmpty(target, nameof(target));

View File

@@ -31,7 +31,7 @@ namespace GitHub.Runner.Worker.Handlers
} }
else if (stage == ActionRunStage.Post) else if (stage == ActionRunStage.Post)
{ {
plugin = Data.Cleanup; plugin = Data.Post;
} }
ArgUtil.NotNullOrEmpty(plugin, nameof(plugin)); ArgUtil.NotNullOrEmpty(plugin, nameof(plugin));

View File

@@ -197,8 +197,8 @@ namespace GitHub.Runner.Worker
// Download actions not already in the cache // Download actions not already in the cache
Trace.Info("Downloading actions"); Trace.Info("Downloading actions");
var actionManager = HostContext.GetService<IActionManager>(); var actionManager = HostContext.GetService<IActionManager>();
var prepareSteps = await actionManager.PrepareActionsAsync(context, message.Steps); var prepareResult = await actionManager.PrepareActionsAsync(context, message.Steps);
preJobSteps.AddRange(prepareSteps); preJobSteps.AddRange(prepareResult.ContainerSetupSteps);
// Add start-container steps, record and stop-container steps // Add start-container steps, record and stop-container steps
if (jobContext.Container != null || jobContext.ServiceContainers.Count > 0) if (jobContext.Container != null || jobContext.ServiceContainers.Count > 0)
@@ -239,9 +239,23 @@ namespace GitHub.Runner.Worker
actionRunner.TryEvaluateDisplayName(contextData, context); actionRunner.TryEvaluateDisplayName(contextData, context);
jobSteps.Add(actionRunner); 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 // Create execution context for pre-job steps
foreach (var step in preJobSteps) foreach (var step in preJobSteps)
{ {
@@ -252,6 +266,12 @@ namespace GitHub.Runner.Worker
Guid stepId = Guid.NewGuid(); Guid stepId = Guid.NewGuid();
extensionStep.ExecutionContext = jobContext.CreateChild(stepId, extensionStep.DisplayName, null, null, stepId.ToString("N")); 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 // Create execution context for job steps
@@ -260,7 +280,8 @@ namespace GitHub.Runner.Worker
if (step is IActionRunner actionStep) if (step is IActionRunner actionStep)
{ {
ArgUtil.NotNull(actionStep, step.DisplayName); 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);
} }
} }

View File

@@ -43,6 +43,8 @@
"entrypoint": "non-empty-string", "entrypoint": "non-empty-string",
"args": "container-runs-args", "args": "container-runs-args",
"env": "container-runs-env", "env": "container-runs-env",
"pre-entrypoint": "non-empty-string",
"pre-if": "non-empty-string",
"post-entrypoint": "non-empty-string", "post-entrypoint": "non-empty-string",
"post-if": "non-empty-string" "post-if": "non-empty-string"
} }
@@ -67,6 +69,8 @@
"properties": { "properties": {
"using": "non-empty-string", "using": "non-empty-string",
"main": "non-empty-string", "main": "non-empty-string",
"pre": "non-empty-string",
"pre-if": "non-empty-string",
"post": "non-empty-string", "post": "non-empty-string",
"post-if": "non-empty-string" "post-if": "non-empty-string"
} }

View File

@@ -660,7 +660,7 @@ namespace GitHub.Runner.Common.Tests.Listener
_settings.AgentId)) _settings.AgentId))
.Returns(async () => .Returns(async () =>
{ {
await Task.Delay(10); await Task.Delay(100);
return "https://t.server"; return "https://t.server";
}); });

View File

@@ -56,7 +56,7 @@ namespace GitHub.Runner.Common.Tests.Worker
}; };
//Act //Act
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
//Assert //Assert
Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]); Assert.Equal(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
@@ -217,7 +217,7 @@ runs:
}; };
//Act //Act
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
Assert.True(steps.Count == 0); Assert.True(steps.Count == 0);
} }
@@ -256,7 +256,7 @@ runs:
var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfile"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfile");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
Assert.Equal(Path.Combine(actionDir, "Dockerfile"), (steps[0].Data as ContainerSetupInfo).Container.Dockerfile); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfileinrelativepath");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithdockerfileinrelativepath");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionfile_DockerfileRelativePath");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionfile_DockerHubImage");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal("ubuntu:18.04", (steps[0].Data as ContainerSetupInfo).Container.Image); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "RepositoryActionWithActionYamlFile_DockerHubImage");
//Act //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((steps[0].Data as ContainerSetupInfo).StepIds[0], actionId);
Assert.Equal("ubuntu:18.04", (steps[0].Data as ContainerSetupInfo).Container.Image); 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"); var actionDir = Path.Combine(_hc.GetDirectory(WellKnownDirectory.Actions), "TingluoHuang", "runner_L0", "repositoryactionwithactionfileanddockerfile");
//Act //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(actionId, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory); Assert.Equal(actionDir, (steps[0].Data as ContainerSetupInfo).Container.WorkingDirectory);
@@ -610,7 +610,7 @@ runs:
}; };
//Act //Act
var steps = await _actionManager.PrepareActionsAsync(_ec.Object, actions); var steps = (await _actionManager.PrepareActionsAsync(_ec.Object, actions)).ContainerSetupSteps;
//Assert //Assert
Assert.Equal(actionId1, (steps[0].Data as ContainerSetupInfo).StepIds[0]); Assert.Equal(actionId1, (steps[0].Data as ContainerSetupInfo).StepIds[0]);
@@ -671,7 +671,7 @@ runs:
}; };
//Act //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. // node.js based action doesn't need any extra steps to build/pull containers.
Assert.True(steps.Count == 0); 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<Pipelines.ActionStep>
{
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<IActionRunner>(new Mock<IActionRunner>().Object);
_hc.EnqueueInstance<IActionRunner>(new Mock<IActionRunner>().Object);
var actionId1 = Guid.NewGuid();
var actionId2 = Guid.NewGuid();
_hc.GetTrace().Info(actionId1);
_hc.GetTrace().Info(actionId2);
var actions = new List<Pipelines.ActionStep>
{
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] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
@@ -1426,7 +1524,7 @@ runs:
Assert.NotNull((definition.Data.Execution as NodeJSActionExecutionData)); Assert.NotNull((definition.Data.Execution as NodeJSActionExecutionData));
Assert.Equal("task.js", (definition.Data.Execution as NodeJSActionExecutionData).Script); 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 finally
{ {
@@ -1506,7 +1604,7 @@ runs:
Assert.NotNull((definition.Data.Execution as ContainerActionExecutionData)); // execution.Node Assert.NotNull((definition.Data.Execution as ContainerActionExecutionData)); // execution.Node
Assert.Equal("image:1234", (definition.Data.Execution as ContainerActionExecutionData).Image); Assert.Equal("image:1234", (definition.Data.Execution as ContainerActionExecutionData).Image);
Assert.Equal("main.sh", (definition.Data.Execution as ContainerActionExecutionData).EntryPoint); 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) foreach (var arg in (definition.Data.Execution as ContainerActionExecutionData).Arguments)
{ {
@@ -1595,7 +1693,7 @@ runs:
Assert.NotNull((definition.Data.Execution as PluginActionExecutionData)); Assert.NotNull((definition.Data.Execution as PluginActionExecutionData));
Assert.Equal("plugin.class, plugin", (definition.Data.Execution as PluginActionExecutionData).Plugin); 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 finally
{ {

View File

@@ -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] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
@@ -97,7 +143,7 @@ namespace GitHub.Runner.Common.Tests.Worker
Assert.Equal("Dockerfile", containerAction.Image); Assert.Equal("Dockerfile", containerAction.Image);
Assert.Equal("main.sh", containerAction.EntryPoint); 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("failure()", containerAction.CleanupCondition);
Assert.Equal("bzz", containerAction.Arguments[0].ToString()); Assert.Equal("bzz", containerAction.Arguments[0].ToString());
Assert.Equal("Token", containerAction.Environment[0].Key.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] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
@@ -143,7 +235,7 @@ namespace GitHub.Runner.Common.Tests.Worker
Assert.Equal("Dockerfile", containerAction.Image); Assert.Equal("Dockerfile", containerAction.Image);
Assert.Equal("main.sh", containerAction.EntryPoint); 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("always()", containerAction.CleanupCondition);
Assert.Equal("bzz", containerAction.Arguments[0].ToString()); Assert.Equal("bzz", containerAction.Arguments[0].ToString());
Assert.Equal("Token", containerAction.Environment[0].Key.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] [Fact]
[Trait("Level", "L0")] [Trait("Level", "L0")]
[Trait("Category", "Worker")] [Trait("Category", "Worker")]
@@ -358,7 +538,7 @@ namespace GitHub.Runner.Common.Tests.Worker
var nodeAction = result.Execution as NodeJSActionExecutionData; var nodeAction = result.Execution as NodeJSActionExecutionData;
Assert.Equal("main.js", nodeAction.Script); Assert.Equal("main.js", nodeAction.Script);
Assert.Equal("cleanup.js", nodeAction.Cleanup); Assert.Equal("cleanup.js", nodeAction.Post);
Assert.Equal("cancelled()", nodeAction.CleanupCondition); Assert.Equal("cancelled()", nodeAction.CleanupCondition);
} }
finally finally
@@ -402,7 +582,7 @@ namespace GitHub.Runner.Common.Tests.Worker
var nodeAction = result.Execution as NodeJSActionExecutionData; var nodeAction = result.Execution as NodeJSActionExecutionData;
Assert.Equal("main.js", nodeAction.Script); Assert.Equal("main.js", nodeAction.Script);
Assert.Equal("cleanup.js", nodeAction.Cleanup); Assert.Equal("cleanup.js", nodeAction.Post);
Assert.Equal("always()", nodeAction.CleanupCondition); Assert.Equal("always()", nodeAction.CleanupCondition);
} }
finally finally

View File

@@ -199,20 +199,20 @@ namespace GitHub.Runner.Common.Tests.Worker
var postRunner1 = hc.CreateService<IActionRunner>(); var postRunner1 = hc.CreateService<IActionRunner>();
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.Stage = ActionRunStage.Post;
postRunner1.Condition = "always()"; postRunner1.Condition = "always()";
postRunner1.DisplayName = "post1"; postRunner1.DisplayName = "post1";
var postRunner2 = hc.CreateService<IActionRunner>(); var postRunner2 = hc.CreateService<IActionRunner>();
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.Stage = ActionRunStage.Post;
postRunner2.Condition = "always()"; postRunner2.Condition = "always()";
postRunner2.DisplayName = "post2"; postRunner2.DisplayName = "post2";
action1.RegisterPostJobStep("post1", postRunner1); action1.RegisterPostJobStep(postRunner1);
action2.RegisterPostJobStep("post2", postRunner2); action2.RegisterPostJobStep(postRunner2);
Assert.NotNull(jobContext.JobSteps); Assert.NotNull(jobContext.JobSteps);
Assert.NotNull(jobContext.PostJobSteps); 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<string, VariableValue>(), new List<MaskHint>(), new Pipelines.JobResources(), new Pipelines.ContextData.DictionaryContextData(), new Pipelines.WorkspaceOptions(), new List<Pipelines.ActionStep>(), 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<IPagingLogger>();
var pagingLogger2 = new Mock<IPagingLogger>();
var pagingLogger3 = new Mock<IPagingLogger>();
var pagingLogger4 = new Mock<IPagingLogger>();
var pagingLogger5 = new Mock<IPagingLogger>();
var jobServerQueue = new Mock<IJobServerQueue>();
jobServerQueue.Setup(x => x.QueueTimelineRecordUpdate(It.IsAny<Guid>(), It.IsAny<TimelineRecord>()));
jobServerQueue.Setup(x => x.QueueWebConsoleLine(It.IsAny<Guid>(), It.IsAny<string>())).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<IActionRunner>();
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<IActionRunner>();
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 = "") private TestHostContext CreateTestContext([CallerMemberName] String testName = "")
{ {
var hc = new TestHostContext(this, testName); var hc = new TestHostContext(this, testName);

View File

@@ -141,7 +141,7 @@ namespace GitHub.Runner.Common.Tests.Worker
jobExtension.Initialize(hc); jobExtension.Initialize(hc);
_actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>())) _actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>()))
.Returns(Task.FromResult(new List<JobExtensionRunner>())); .Returns(Task.FromResult(new PrepareResult(new List<JobExtensionRunner>(), new Dictionary<Guid, IActionRunner>())));
List<IStep> result = await jobExtension.InitializeJob(_jobEc, _message); List<IStep> result = await jobExtension.InitializeJob(_jobEc, _message);
@@ -176,7 +176,7 @@ namespace GitHub.Runner.Common.Tests.Worker
jobExtension.Initialize(hc); jobExtension.Initialize(hc);
_actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>())) _actionManager.Setup(x => x.PrepareActionsAsync(It.IsAny<IExecutionContext>(), It.IsAny<IEnumerable<Pipelines.JobStep>>()))
.Returns(Task.FromResult(new List<JobExtensionRunner>() { new JobExtensionRunner(null, "", "prepare1", null), new JobExtensionRunner(null, "", "prepare2", null) })); .Returns(Task.FromResult(new PrepareResult(new List<JobExtensionRunner>() { new JobExtensionRunner(null, "", "prepare1", null), new JobExtensionRunner(null, "", "prepare2", null) }, new Dictionary<Guid, IActionRunner>())));
List<IStep> result = await jobExtension.InitializeJob(_jobEc, _message); List<IStep> result = await jobExtension.InitializeJob(_jobEc, _message);

View File

@@ -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()'

View File

@@ -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'

View File

@@ -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()'

View File

@@ -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'