Detect Makefiles and parse out the targets

This commit is contained in:
Brian Cristante
2021-10-14 11:56:26 -04:00
parent a5a0f8df47
commit 9ca132380c
5 changed files with 167 additions and 10 deletions

View File

@@ -10,6 +10,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.Runner.Common;
using GitHub.Runner.Sdk;
using GitHub.Runner.Worker.Container;
@@ -486,9 +487,45 @@ namespace GitHub.Runner.Worker
}
else if (action.Reference.Type == Pipelines.ActionSourceType.Script)
{
definition.Data.Execution = new ScriptActionExecutionData();
definition.Data.Name = "Run";
definition.Data.Description = "Execute a script";
// Load the inputs.
executionContext.Debug("Loading inputs");
var templateEvaluator = executionContext.ToPipelineTemplateEvaluator();
var inputs = templateEvaluator.EvaluateStepInputs(action.Inputs, executionContext.ExpressionValues, executionContext.ExpressionFunctions);
// Check if we are running a Makefile.
// Only works for the default target right now.
if (inputs["script"] == "make")
{
// Get the path of the Makefile in the repository root.
var githubContext = executionContext.ExpressionValues["github"] as GitHubContext;
var workspaceDir = githubContext["workspace"] as StringContextData;
var makefile = Path.Combine(workspaceDir, "Makefile");
if (!File.Exists(makefile))
{
// Forget about trying to be smart. Just do the normal thing.
definition.Data.Execution = new ScriptActionExecutionData();
definition.Data.Name = "Run";
definition.Data.Description = "Execute a script";
}
// Assume the default target is named `all`.
var definitionData = MakefileManager.Load(executionContext, makefile, target: "all");
if (definitionData is null)
{
// Forget about trying to be smart. Just do the normal thing.
definition.Data.Execution = new ScriptActionExecutionData();
definition.Data.Name = "Run";
definition.Data.Description = "Execute a script";
}
definition.Data = definitionData;
}
else
{
definition.Data.Execution = new ScriptActionExecutionData();
definition.Data.Name = "Run";
definition.Data.Description = "Execute a script";
}
}
else
{
@@ -1143,6 +1180,7 @@ namespace GitHub.Runner.Worker
Plugin,
Script,
Composite,
Makefile,
}
public sealed class ContainerActionExecutionData : ActionExecutionData
@@ -1210,6 +1248,14 @@ namespace GitHub.Runner.Worker
public MappingToken Outputs { get; set; }
}
public sealed class MakefileExecutionData : ActionExecutionData
{
public override ActionExecutionType ExecutionType => ActionExecutionType.Makefile;
public override bool HasPre => false;
public override bool HasPost => false;
public List<string> Targets { get; set; }
}
public abstract class ActionExecutionData
{
private string _initCondition = $"{Constants.Expressions.Always}()";

View File

@@ -73,6 +73,11 @@ namespace GitHub.Runner.Worker.Handlers
handler = HostContext.CreateService<ICompositeActionHandler>();
(handler as ICompositeActionHandler).Data = data as CompositeActionExecutionData;
}
else if (data.ExecutionType == ActionExecutionType.Makefile)
{
handler = HostContext.CreateService<IMakefileHandler>();
(handler as IMakefileHandler).Data = data as MakefileExecutionData;
}
else
{
// This should never happen.

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using GitHub.DistributedTask.Expressions2;
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using GitHub.DistributedTask.Pipelines.ContextData;
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
using GitHub.DistributedTask.WebApi;
using GitHub.Runner.Common;
using GitHub.Runner.Common.Util;
using GitHub.Runner.Sdk;
using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Expressions;
using Pipelines = GitHub.DistributedTask.Pipelines;
namespace GitHub.Runner.Worker.Handlers
{
[ServiceLocator(Default = typeof(MakefileHandler))]
public interface IMakefileHandler : IHandler
{
MakefileExecutionData Data { get; set; }
}
public sealed class MakefileHandler : Handler, IMakefileHandler
{
public MakefileExecutionData Data { get; set; }
public async Task RunAsync(ActionRunStage stage)
{
// Validate args
Trace.Entering();
ArgUtil.NotNull(ExecutionContext, nameof(ExecutionContext));
ArgUtil.NotNull(Inputs, nameof(Inputs));
// Create a script handler for each target
var handlers = Data.Targets.Select(target =>
{
var handler = HostContext.CreateService<IScriptHandler>();
// IScriptHandler does not need .Action
// handler.Action = action;
handler.Data = new ScriptActionExecutionData();
handler.Environment = Environment;
handler.RuntimeVariables = RuntimeVariables;
handler.ExecutionContext = ExecutionContext;
handler.StepHost = StepHost;
handler.Inputs = new Dictionary<string, string>
{
["script"] = $"make {target}"
};
handler.ActionDirectory = ActionDirectory;
handler.LocalActionContainerSetupSteps = LocalActionContainerSetupSteps;
return handler;
});
foreach (var handler in handlers)
{
await handler.RunAsync(stage);
}
}
}
}

View File

@@ -31,8 +31,8 @@ namespace GitHub.Runner.Worker.Handlers
Inputs.TryGetValue("script", out string contents);
contents = contents ?? string.Empty;
if (Action.Type == Pipelines.ActionSourceType.Script)
{
// if (Action.Type == Pipelines.ActionSourceType.Script)
// {
var firstLine = contents.TrimStart(' ', '\t', '\r', '\n');
var firstNewLine = firstLine.IndexOfAny(new[] { '\r', '\n' });
if (firstNewLine >= 0)
@@ -41,11 +41,11 @@ namespace GitHub.Runner.Worker.Handlers
}
ExecutionContext.Output($"##[group]Run {firstLine}");
}
else
{
throw new InvalidOperationException($"Invalid action type {Action.Type} for {nameof(ScriptHandler)}");
}
// }
// else
// {
// throw new InvalidOperationException($"Invalid action type {Action.Type} for {nameof(ScriptHandler)}");
// }
var multiLines = contents.Replace("\r\n", "\n").TrimEnd('\n').Split('\n');
foreach (var line in multiLines)

View File

@@ -0,0 +1,39 @@
using System.IO;
using System.Linq;
namespace GitHub.Runner.Worker
{
public static class MakefileManager
{
// Convert the `all` target to a set of steps of its dependencies.
// Does not recurse into the dependencies of those steps.
public static ActionDefinitionData Load(IExecutionContext executionContext, string makefile, string target)
{
var targetToFind = target + ":";
var lines = File.ReadLines(makefile);
string targetLine = lines.FirstOrDefault(line => line.TrimStart().StartsWith(targetToFind));
if (targetLine is null)
{
return null;
}
var dependencies = targetLine.Split().Skip(1).ToList();
if (dependencies.Count == 0)
{
return null;
}
return new ActionDefinitionData
{
Name = $"make {target}",
Description = "Execute a Makefile target",
Execution = new MakefileExecutionData
{
Targets = dependencies,
InitCondition = "always()",
CleanupCondition = "always()",
}
};
}
}
}