mirror of
https://github.com/actions/runner.git
synced 2025-12-12 05:37:01 +00:00
Compare commits
6 Commits
users/etha
...
users/logo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ceb092f7f3 | ||
|
|
e728b8594d | ||
|
|
de4490d06d | ||
|
|
2e800f857e | ||
|
|
312c7668a8 | ||
|
|
eaf39bb058 |
@@ -70,8 +70,8 @@ then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# libicu version prefer: libicu63 -> libicu60 -> libicu57 -> libicu55 -> libicu52
|
# libicu version prefer: libicu66 -> libicu63 -> libicu60 -> libicu57 -> libicu55 -> libicu52
|
||||||
apt install -y libicu63 || apt install -y libicu60 || apt install -y libicu57 || apt install -y libicu55 || apt install -y libicu52
|
apt install -y libicu66 || apt install -y libicu63 || apt install -y libicu60 || apt install -y libicu57 || apt install -y libicu55 || apt install -y libicu52
|
||||||
if [ $? -ne 0 ]
|
if [ $? -ne 0 ]
|
||||||
then
|
then
|
||||||
echo "'apt' failed with exit code '$?'"
|
echo "'apt' failed with exit code '$?'"
|
||||||
@@ -99,8 +99,8 @@ then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# libicu version prefer: libicu63 -> libicu60 -> libicu57 -> libicu55 -> libicu52
|
# libicu version prefer: libicu66 -> libicu63 -> libicu60 -> libicu57 -> libicu55 -> libicu52
|
||||||
apt-get install -y libicu63 || apt-get install -y libicu60 || apt install -y libicu57 || apt install -y libicu55 || apt install -y libicu52
|
apt-get install -y libicu66 || apt-get install -y libicu63 || apt-get install -y libicu60 || apt install -y libicu57 || apt install -y libicu55 || apt install -y libicu52
|
||||||
if [ $? -ne 0 ]
|
if [ $? -ne 0 ]
|
||||||
then
|
then
|
||||||
echo "'apt-get' failed with exit code '$?'"
|
echo "'apt-get' failed with exit code '$?'"
|
||||||
|
|||||||
@@ -63,12 +63,25 @@ function install()
|
|||||||
|
|
||||||
sed "s/{{User}}/${run_as_user}/g; s/{{Description}}/$(echo ${SVC_DESCRIPTION} | sed -e 's/[\/&]/\\&/g')/g; s/{{RunnerRoot}}/$(echo ${RUNNER_ROOT} | sed -e 's/[\/&]/\\&/g')/g;" "${TEMPLATE_PATH}" > "${TEMP_PATH}" || failed "failed to create replacement temp file"
|
sed "s/{{User}}/${run_as_user}/g; s/{{Description}}/$(echo ${SVC_DESCRIPTION} | sed -e 's/[\/&]/\\&/g')/g; s/{{RunnerRoot}}/$(echo ${RUNNER_ROOT} | sed -e 's/[\/&]/\\&/g')/g;" "${TEMPLATE_PATH}" > "${TEMP_PATH}" || failed "failed to create replacement temp file"
|
||||||
mv "${TEMP_PATH}" "${UNIT_PATH}" || failed "failed to copy unit file"
|
mv "${TEMP_PATH}" "${UNIT_PATH}" || failed "failed to copy unit file"
|
||||||
|
|
||||||
|
# Recent Fedora based Linux (CentOS/Redhat) has SELinux enabled by default
|
||||||
|
# We need to restore security context on the unit file we added otherwise SystemD have no access to it.
|
||||||
|
command -v getenforce > /dev/null
|
||||||
|
if [ $? -eq 0 ]
|
||||||
|
then
|
||||||
|
selinuxEnabled=$(getenforce)
|
||||||
|
if [[ $selinuxEnabled == "Enforcing" ]]
|
||||||
|
then
|
||||||
|
# SELinux is enabled, we will need to Restore SELinux Context for the service file
|
||||||
|
restorecon -r -v "${UNIT_PATH}" || failed "failed to restore SELinux context on ${UNIT_PATH}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# unit file should not be executable and world writable
|
# unit file should not be executable and world writable
|
||||||
chmod 664 ${UNIT_PATH} || failed "failed to set permissions on ${UNIT_PATH}"
|
chmod 664 "${UNIT_PATH}" || failed "failed to set permissions on ${UNIT_PATH}"
|
||||||
systemctl daemon-reload || failed "failed to reload daemons"
|
systemctl daemon-reload || failed "failed to reload daemons"
|
||||||
|
|
||||||
# Since we started with sudo, runsvc.sh will be owned by root. Change this to current login user.
|
# Since we started with sudo, runsvc.sh will be owned by root. Change this to current login user.
|
||||||
cp ./bin/runsvc.sh ./runsvc.sh || failed "failed to copy runsvc.sh"
|
cp ./bin/runsvc.sh ./runsvc.sh || failed "failed to copy runsvc.sh"
|
||||||
chown ${run_as_uid}:${run_as_gid} ./runsvc.sh || failed "failed to set owner for runsvc.sh"
|
chown ${run_as_uid}:${run_as_gid} ./runsvc.sh || failed "failed to set owner for runsvc.sh"
|
||||||
chmod 755 ./runsvc.sh || failed "failed to set permission for runsvc.sh"
|
chmod 755 ./runsvc.sh || failed "failed to set permission for runsvc.sh"
|
||||||
|
|||||||
@@ -210,7 +210,7 @@ namespace GitHub.Runner.Listener.Configuration
|
|||||||
else if (command.Unattended)
|
else if (command.Unattended)
|
||||||
{
|
{
|
||||||
// if not replace and it is unattended config.
|
// if not replace and it is unattended config.
|
||||||
throw new TaskAgentExistsException($"Pool {runnerSettings.PoolId} already contains a runner with name {runnerSettings.AgentName}.");
|
throw new TaskAgentExistsException($"A runner exists with the same name {runnerSettings.AgentName}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -346,14 +346,14 @@ namespace GitHub.Runner.Sdk
|
|||||||
// data buffers one last time before returning
|
// data buffers one last time before returning
|
||||||
ProcessOutput();
|
ProcessOutput();
|
||||||
|
|
||||||
Trace.Info($"Finished process {_proc.Id} with exit code {_proc.ExitCode}, and elapsed time {_stopWatch.Elapsed}.");
|
if (cancellationToken.IsCancellationRequested)
|
||||||
}
|
{
|
||||||
|
// Ensure cancellation also finish on the cancellationToken.Register thread.
|
||||||
|
await cancellationFinished.Task;
|
||||||
|
Trace.Info($"Process Cancellation finished.");
|
||||||
|
}
|
||||||
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
Trace.Info($"Finished process {_proc.Id} with exit code {_proc.ExitCode}, and elapsed time {_stopWatch.Elapsed}.");
|
||||||
{
|
|
||||||
// Ensure cancellation also finish on the cancellationToken.Register thread.
|
|
||||||
await cancellationFinished.Task;
|
|
||||||
Trace.Info($"Process Cancellation finished.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ namespace GitHub.Runner.Sdk
|
|||||||
{
|
{
|
||||||
ArgUtil.NotNullOrEmpty(command, nameof(command));
|
ArgUtil.NotNullOrEmpty(command, nameof(command));
|
||||||
trace?.Info($"Which: '{command}'");
|
trace?.Info($"Which: '{command}'");
|
||||||
|
if (Path.IsPathFullyQualified(command) && File.Exists(command))
|
||||||
|
{
|
||||||
|
trace?.Info($"Fully qualified path: '{command}'");
|
||||||
|
return command;
|
||||||
|
}
|
||||||
string path = Environment.GetEnvironmentVariable(PathUtil.PathVariable);
|
string path = Environment.GetEnvironmentVariable(PathUtil.PathVariable);
|
||||||
if (string.IsNullOrEmpty(path))
|
if (string.IsNullOrEmpty(path))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -395,12 +395,6 @@ namespace GitHub.Runner.Worker
|
|||||||
Trace.Info($"Action cleanup plugin: {plugin.PluginTypeName}.");
|
Trace.Info($"Action cleanup plugin: {plugin.PluginTypeName}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (definition.Data.Execution.ExecutionType == ActionExecutionType.Composite && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
|
||||||
{
|
|
||||||
var compositeAction = definition.Data.Execution as CompositeActionExecutionData;
|
|
||||||
Trace.Info($"Load {compositeAction.Steps.Count} action steps.");
|
|
||||||
Trace.Verbose($"Details: {StringUtil.ConvertToJson(compositeAction.Steps)}");
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotSupportedException(definition.Data.Execution.ExecutionType.ToString());
|
throw new NotSupportedException(definition.Data.Execution.ExecutionType.ToString());
|
||||||
@@ -1107,11 +1101,6 @@ namespace GitHub.Runner.Worker
|
|||||||
Trace.Info($"Action plugin: {(actionDefinitionData.Execution as PluginActionExecutionData).Plugin}, no more preparation.");
|
Trace.Info($"Action plugin: {(actionDefinitionData.Execution as PluginActionExecutionData).Plugin}, no more preparation.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else if (actionDefinitionData.Execution.ExecutionType == ActionExecutionType.Composite && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
|
||||||
{
|
|
||||||
Trace.Info($"Action composite: {(actionDefinitionData.Execution as CompositeActionExecutionData).Steps}, no more preparation.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotSupportedException(actionDefinitionData.Execution.ExecutionType.ToString());
|
throw new NotSupportedException(actionDefinitionData.Execution.ExecutionType.ToString());
|
||||||
@@ -1222,7 +1211,6 @@ namespace GitHub.Runner.Worker
|
|||||||
NodeJS,
|
NodeJS,
|
||||||
Plugin,
|
Plugin,
|
||||||
Script,
|
Script,
|
||||||
Composite,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ContainerActionExecutionData : ActionExecutionData
|
public sealed class ContainerActionExecutionData : ActionExecutionData
|
||||||
@@ -1279,14 +1267,6 @@ namespace GitHub.Runner.Worker
|
|||||||
public override bool HasPost => false;
|
public override bool HasPost => false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class CompositeActionExecutionData : ActionExecutionData
|
|
||||||
{
|
|
||||||
public override ActionExecutionType ExecutionType => ActionExecutionType.Composite;
|
|
||||||
public override bool HasPre => false;
|
|
||||||
public override bool HasPost => false;
|
|
||||||
public List<Pipelines.ActionStep> Steps { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class ActionExecutionData
|
public abstract class ActionExecutionData
|
||||||
{
|
{
|
||||||
private string _initCondition = $"{Constants.Expressions.Always}()";
|
private string _initCondition = $"{Constants.Expressions.Always}()";
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ using YamlDotNet.Core;
|
|||||||
using YamlDotNet.Core.Events;
|
using YamlDotNet.Core.Events;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker
|
namespace GitHub.Runner.Worker
|
||||||
{
|
{
|
||||||
@@ -93,7 +92,7 @@ namespace GitHub.Runner.Worker
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case "runs":
|
case "runs":
|
||||||
actionDefinition.Execution = ConvertRuns(executionContext, context, actionPair.Value);
|
actionDefinition.Execution = ConvertRuns(context, actionPair.Value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Trace.Info($"Ignore action property {propertyName}.");
|
Trace.Info($"Ignore action property {propertyName}.");
|
||||||
@@ -285,7 +284,7 @@ namespace GitHub.Runner.Worker
|
|||||||
// Add the file table
|
// Add the file table
|
||||||
if (_fileTable?.Count > 0)
|
if (_fileTable?.Count > 0)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < _fileTable.Count; i++)
|
for (var i = 0 ; i < _fileTable.Count ; i++)
|
||||||
{
|
{
|
||||||
result.GetFileId(_fileTable[i]);
|
result.GetFileId(_fileTable[i]);
|
||||||
}
|
}
|
||||||
@@ -295,7 +294,6 @@ namespace GitHub.Runner.Worker
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ActionExecutionData ConvertRuns(
|
private ActionExecutionData ConvertRuns(
|
||||||
IExecutionContext executionContext,
|
|
||||||
TemplateContext context,
|
TemplateContext context,
|
||||||
TemplateToken inputsToken)
|
TemplateToken inputsToken)
|
||||||
{
|
{
|
||||||
@@ -313,8 +311,6 @@ namespace GitHub.Runner.Worker
|
|||||||
var postToken = default(StringToken);
|
var postToken = default(StringToken);
|
||||||
var postEntrypointToken = default(StringToken);
|
var postEntrypointToken = default(StringToken);
|
||||||
var postIfToken = default(StringToken);
|
var postIfToken = default(StringToken);
|
||||||
var stepsLoaded = default(List<Pipelines.ActionStep>);
|
|
||||||
|
|
||||||
foreach (var run in runsMapping)
|
foreach (var run in runsMapping)
|
||||||
{
|
{
|
||||||
var runsKey = run.Key.AssertString("runs key").Value;
|
var runsKey = run.Key.AssertString("runs key").Value;
|
||||||
@@ -359,15 +355,6 @@ namespace GitHub.Runner.Worker
|
|||||||
case "pre-if":
|
case "pre-if":
|
||||||
preIfToken = run.Value.AssertString("pre-if");
|
preIfToken = run.Value.AssertString("pre-if");
|
||||||
break;
|
break;
|
||||||
case "steps":
|
|
||||||
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
|
||||||
{
|
|
||||||
var steps = run.Value.AssertSequence("steps");
|
|
||||||
var evaluator = executionContext.ToPipelineTemplateEvaluator();
|
|
||||||
stepsLoaded = evaluator.LoadCompositeSteps(steps);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
throw new Exception("You aren't supposed to be using Composite Actions yet!");
|
|
||||||
default:
|
default:
|
||||||
Trace.Info($"Ignore run property {runsKey}.");
|
Trace.Info($"Ignore run property {runsKey}.");
|
||||||
break;
|
break;
|
||||||
@@ -415,21 +402,6 @@ namespace GitHub.Runner.Worker
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (string.Equals(usingToken.Value, "composite", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
|
||||||
{
|
|
||||||
if (stepsLoaded == null)
|
|
||||||
{
|
|
||||||
// TODO: Add a more helpful error message + including file name, etc. to show user that it's because of their yaml file
|
|
||||||
throw new ArgumentNullException($"No steps provided.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return new CompositeActionExecutionData()
|
|
||||||
{
|
|
||||||
Steps = stepsLoaded,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker' or 'node12' instead.");
|
throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker' or 'node12' instead.");
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ namespace GitHub.Runner.Worker
|
|||||||
JobContext JobContext { get; }
|
JobContext JobContext { get; }
|
||||||
|
|
||||||
// Only job level ExecutionContext has JobSteps
|
// Only job level ExecutionContext has JobSteps
|
||||||
List<IStep> JobSteps { get; }
|
Queue<IStep> JobSteps { get; }
|
||||||
|
|
||||||
// Only job level ExecutionContext has PostJobSteps
|
// Only job level ExecutionContext has PostJobSteps
|
||||||
Stack<IStep> PostJobSteps { get; }
|
Stack<IStep> PostJobSteps { get; }
|
||||||
@@ -105,7 +105,6 @@ namespace GitHub.Runner.Worker
|
|||||||
// others
|
// others
|
||||||
void ForceTaskComplete();
|
void ForceTaskComplete();
|
||||||
void RegisterPostJobStep(IStep step);
|
void RegisterPostJobStep(IStep step);
|
||||||
void RegisterNestedStep(IStep step, DictionaryContextData inputsData, int location);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
||||||
@@ -160,7 +159,7 @@ namespace GitHub.Runner.Worker
|
|||||||
public List<ContainerInfo> ServiceContainers { get; private set; }
|
public List<ContainerInfo> ServiceContainers { get; private set; }
|
||||||
|
|
||||||
// Only job level ExecutionContext has JobSteps
|
// Only job level ExecutionContext has JobSteps
|
||||||
public List<IStep> JobSteps { get; private set; }
|
public Queue<IStep> JobSteps { get; private set; }
|
||||||
|
|
||||||
// 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; }
|
||||||
@@ -170,6 +169,7 @@ namespace GitHub.Runner.Worker
|
|||||||
|
|
||||||
public bool EchoOnActionCommand { get; set; }
|
public bool EchoOnActionCommand { get; set; }
|
||||||
|
|
||||||
|
|
||||||
public TaskResult? Result
|
public TaskResult? Result
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -266,20 +266,6 @@ namespace GitHub.Runner.Worker
|
|||||||
Root.PostJobSteps.Push(step);
|
Root.PostJobSteps.Push(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper function used in CompositeActionHandler::RunAsync to
|
|
||||||
/// add a child node, aka a step, to the current job to the Root.JobSteps based on the location.
|
|
||||||
/// </summary>
|
|
||||||
public void RegisterNestedStep(IStep step, DictionaryContextData inputsData, int location)
|
|
||||||
{
|
|
||||||
// TODO: For UI purposes, look at figuring out how to condense steps in one node => maybe use the same previous GUID
|
|
||||||
var newGuid = Guid.NewGuid();
|
|
||||||
step.ExecutionContext = Root.CreateChild(newGuid, step.DisplayName, newGuid.ToString("N"), null, null);
|
|
||||||
step.ExecutionContext.ExpressionValues["inputs"] = inputsData;
|
|
||||||
// TODO: confirm whether not copying message contexts is safe
|
|
||||||
Root.JobSteps.Insert(location, step);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null)
|
public IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null)
|
||||||
{
|
{
|
||||||
Trace.Entering();
|
Trace.Entering();
|
||||||
@@ -674,7 +660,7 @@ namespace GitHub.Runner.Worker
|
|||||||
PrependPath = new List<string>();
|
PrependPath = new List<string>();
|
||||||
|
|
||||||
// JobSteps for job ExecutionContext
|
// JobSteps for job ExecutionContext
|
||||||
JobSteps = new List<IStep>();
|
JobSteps = new Queue<IStep>();
|
||||||
|
|
||||||
// PostJobSteps for job ExecutionContext
|
// PostJobSteps for job ExecutionContext
|
||||||
PostJobSteps = new Stack<IStep>();
|
PostJobSteps = new Stack<IStep>();
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using GitHub.DistributedTask.ObjectTemplating.Tokens;
|
|
||||||
using GitHub.DistributedTask.Pipelines.ContextData;
|
|
||||||
using GitHub.DistributedTask.WebApi;
|
|
||||||
using GitHub.Runner.Common;
|
|
||||||
using GitHub.Runner.Sdk;
|
|
||||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
|
||||||
|
|
||||||
|
|
||||||
namespace GitHub.Runner.Worker.Handlers
|
|
||||||
{
|
|
||||||
[ServiceLocator(Default = typeof(CompositeActionHandler))]
|
|
||||||
public interface ICompositeActionHandler : IHandler
|
|
||||||
{
|
|
||||||
CompositeActionExecutionData Data { get; set; }
|
|
||||||
}
|
|
||||||
public sealed class CompositeActionHandler : Handler, ICompositeActionHandler
|
|
||||||
{
|
|
||||||
public CompositeActionExecutionData Data { get; set; }
|
|
||||||
|
|
||||||
public Task RunAsync(ActionRunStage stage)
|
|
||||||
{
|
|
||||||
// Validate args.
|
|
||||||
Trace.Entering();
|
|
||||||
ArgUtil.NotNull(ExecutionContext, nameof(ExecutionContext));
|
|
||||||
ArgUtil.NotNull(Inputs, nameof(Inputs));
|
|
||||||
|
|
||||||
var githubContext = ExecutionContext.ExpressionValues["github"] as GitHubContext;
|
|
||||||
ArgUtil.NotNull(githubContext, nameof(githubContext));
|
|
||||||
|
|
||||||
var tempDirectory = HostContext.GetDirectory(WellKnownDirectory.Temp);
|
|
||||||
|
|
||||||
// Resolve action steps
|
|
||||||
var actionSteps = Data.Steps;
|
|
||||||
|
|
||||||
// Create Context Data to reuse for each composite action step
|
|
||||||
var inputsData = new DictionaryContextData();
|
|
||||||
foreach (var i in Inputs)
|
|
||||||
{
|
|
||||||
inputsData[i.Key] = new StringContextData(i.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add each composite action step to the front of the queue
|
|
||||||
int location = 0;
|
|
||||||
foreach (Pipelines.ActionStep aStep in actionSteps)
|
|
||||||
{
|
|
||||||
// Ex:
|
|
||||||
// runs:
|
|
||||||
// using: "composite"
|
|
||||||
// steps:
|
|
||||||
// - uses: example/test-composite@v2 (a)
|
|
||||||
// - run echo hello world (b)
|
|
||||||
// - run echo hello world 2 (c)
|
|
||||||
//
|
|
||||||
// ethanchewy/test-composite/action.yaml
|
|
||||||
// runs:
|
|
||||||
// using: "composite"
|
|
||||||
// steps:
|
|
||||||
// - run echo hello world 3 (d)
|
|
||||||
// - run echo hello world 4 (e)
|
|
||||||
//
|
|
||||||
// Steps processed as follow:
|
|
||||||
// | a |
|
|
||||||
// | a | => | d |
|
|
||||||
// (Run step d)
|
|
||||||
// | a |
|
|
||||||
// | a | => | e |
|
|
||||||
// (Run step e)
|
|
||||||
// | a |
|
|
||||||
// (Run step a)
|
|
||||||
// | b |
|
|
||||||
// (Run step b)
|
|
||||||
// | c |
|
|
||||||
// (Run step c)
|
|
||||||
// Done.
|
|
||||||
|
|
||||||
var actionRunner = HostContext.CreateService<IActionRunner>();
|
|
||||||
actionRunner.Action = aStep;
|
|
||||||
actionRunner.Stage = stage;
|
|
||||||
actionRunner.Condition = aStep.Condition;
|
|
||||||
actionRunner.DisplayName = aStep.DisplayName;
|
|
||||||
// TODO: Do we need to add any context data from the job message?
|
|
||||||
// (See JobExtension.cs ~line 236)
|
|
||||||
|
|
||||||
ExecutionContext.RegisterNestedStep(actionRunner, inputsData, location);
|
|
||||||
location++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -66,11 +66,6 @@ namespace GitHub.Runner.Worker.Handlers
|
|||||||
handler = HostContext.CreateService<IRunnerPluginHandler>();
|
handler = HostContext.CreateService<IRunnerPluginHandler>();
|
||||||
(handler as IRunnerPluginHandler).Data = data as PluginActionExecutionData;
|
(handler as IRunnerPluginHandler).Data = data as PluginActionExecutionData;
|
||||||
}
|
}
|
||||||
else if (data.ExecutionType == ActionExecutionType.Composite)
|
|
||||||
{
|
|
||||||
handler = HostContext.CreateService<ICompositeActionHandler>();
|
|
||||||
(handler as ICompositeActionHandler).Data = data as CompositeActionExecutionData;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This should never happen.
|
// This should never happen.
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ namespace GitHub.Runner.Worker
|
|||||||
{
|
{
|
||||||
foreach (var step in jobSteps)
|
foreach (var step in jobSteps)
|
||||||
{
|
{
|
||||||
jobContext.JobSteps.Add(step);
|
jobContext.JobSteps.Enqueue(step);
|
||||||
}
|
}
|
||||||
|
|
||||||
await stepsRunner.RunAsync(jobContext);
|
await stepsRunner.RunAsync(jobContext);
|
||||||
|
|||||||
@@ -59,15 +59,14 @@ namespace GitHub.Runner.Worker
|
|||||||
checkPostJobActions = true;
|
checkPostJobActions = true;
|
||||||
while (jobContext.PostJobSteps.TryPop(out var postStep))
|
while (jobContext.PostJobSteps.TryPop(out var postStep))
|
||||||
{
|
{
|
||||||
jobContext.JobSteps.Add(postStep);
|
jobContext.JobSteps.Enqueue(postStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var step = jobContext.JobSteps[0];
|
var step = jobContext.JobSteps.Dequeue();
|
||||||
jobContext.JobSteps.RemoveAt(0);
|
var nextStep = jobContext.JobSteps.Count > 0 ? jobContext.JobSteps.Peek() : null;
|
||||||
var nextStep = jobContext.JobSteps.Count > 0 ? jobContext.JobSteps[0] : null;
|
|
||||||
|
|
||||||
Trace.Info($"Processing step: DisplayName='{step.DisplayName}'");
|
Trace.Info($"Processing step: DisplayName='{step.DisplayName}'");
|
||||||
ArgUtil.NotNull(step.ExecutionContext, nameof(step.ExecutionContext));
|
ArgUtil.NotNull(step.ExecutionContext, nameof(step.ExecutionContext));
|
||||||
@@ -410,11 +409,7 @@ namespace GitHub.Runner.Worker
|
|||||||
scope = scopesToInitialize.Pop();
|
scope = scopesToInitialize.Pop();
|
||||||
executionContext.Debug($"Initializing scope '{scope.Name}'");
|
executionContext.Debug($"Initializing scope '{scope.Name}'");
|
||||||
executionContext.ExpressionValues["steps"] = stepsContext.GetScope(scope.ParentName);
|
executionContext.ExpressionValues["steps"] = stepsContext.GetScope(scope.ParentName);
|
||||||
// TODO: Fix this temporary workaround for Composite Actions
|
executionContext.ExpressionValues["inputs"] = !String.IsNullOrEmpty(scope.ParentName) ? scopeInputs[scope.ParentName] : null;
|
||||||
if (!executionContext.ExpressionValues.ContainsKey("inputs") && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
|
||||||
{
|
|
||||||
executionContext.ExpressionValues["inputs"] = !String.IsNullOrEmpty(scope.ParentName) ? scopeInputs[scope.ParentName] : null;
|
|
||||||
}
|
|
||||||
var templateEvaluator = executionContext.ToPipelineTemplateEvaluator();
|
var templateEvaluator = executionContext.ToPipelineTemplateEvaluator();
|
||||||
var inputs = default(DictionaryContextData);
|
var inputs = default(DictionaryContextData);
|
||||||
try
|
try
|
||||||
@@ -437,11 +432,7 @@ namespace GitHub.Runner.Worker
|
|||||||
// Setup expression values
|
// Setup expression values
|
||||||
var scopeName = executionContext.ScopeName;
|
var scopeName = executionContext.ScopeName;
|
||||||
executionContext.ExpressionValues["steps"] = stepsContext.GetScope(scopeName);
|
executionContext.ExpressionValues["steps"] = stepsContext.GetScope(scopeName);
|
||||||
// TODO: Fix this temporary workaround for Composite Actions
|
executionContext.ExpressionValues["inputs"] = string.IsNullOrEmpty(scopeName) ? null : scopeInputs[scopeName];
|
||||||
if (!executionContext.ExpressionValues.ContainsKey("inputs") && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
|
||||||
{
|
|
||||||
executionContext.ExpressionValues["inputs"] = string.IsNullOrEmpty(scopeName) ? null : scopeInputs[scopeName];
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,8 +32,7 @@
|
|||||||
"one-of": [
|
"one-of": [
|
||||||
"container-runs",
|
"container-runs",
|
||||||
"node12-runs",
|
"node12-runs",
|
||||||
"plugin-runs",
|
"plugin-runs"
|
||||||
"composite-runs"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"container-runs": {
|
"container-runs": {
|
||||||
@@ -84,36 +83,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"composite-runs": {
|
|
||||||
"mapping": {
|
|
||||||
"properties": {
|
|
||||||
"using": "non-empty-string",
|
|
||||||
"steps": "composite-steps"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"composite-steps": {
|
|
||||||
"context": [
|
|
||||||
"github",
|
|
||||||
"needs",
|
|
||||||
"strategy",
|
|
||||||
"matrix",
|
|
||||||
"secrets",
|
|
||||||
"steps",
|
|
||||||
"inputs",
|
|
||||||
"job",
|
|
||||||
"runner",
|
|
||||||
"env",
|
|
||||||
"always(0,0)",
|
|
||||||
"failure(0,0)",
|
|
||||||
"cancelled(0,0)",
|
|
||||||
"success(0,0)",
|
|
||||||
"hashFiles(1,255)"
|
|
||||||
],
|
|
||||||
"sequence": {
|
|
||||||
"item-type": "any"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"container-runs-context": {
|
"container-runs-context": {
|
||||||
"context": [
|
"context": [
|
||||||
"inputs"
|
"inputs"
|
||||||
|
|||||||
@@ -65,7 +65,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
public const String StepEnv = "step-env";
|
public const String StepEnv = "step-env";
|
||||||
public const String StepIfResult = "step-if-result";
|
public const String StepIfResult = "step-if-result";
|
||||||
public const String Steps = "steps";
|
public const String Steps = "steps";
|
||||||
public const String StepsInTemplate = "steps-in-template";
|
|
||||||
public const String StepsScopeInputs = "steps-scope-inputs";
|
public const String StepsScopeInputs = "steps-scope-inputs";
|
||||||
public const String StepsScopeOutputs = "steps-scope-outputs";
|
public const String StepsScopeOutputs = "steps-scope-outputs";
|
||||||
public const String StepsTemplateRoot = "steps-template-root";
|
public const String StepsTemplateRoot = "steps-template-root";
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
var evaluationResult = EvaluationResult.CreateIntermediateResult(null, ifResult);
|
var evaluationResult = EvaluationResult.CreateIntermediateResult(null, ifResult);
|
||||||
return evaluationResult.IsTruthy;
|
return evaluationResult.IsTruthy;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Boolean? ConvertToStepContinueOnError(
|
internal static Boolean? ConvertToStepContinueOnError(
|
||||||
TemplateContext context,
|
TemplateContext context,
|
||||||
TemplateToken token,
|
TemplateToken token,
|
||||||
@@ -263,351 +264,5 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Note: originally was List<Step> but we need to change to List<ActionStep> to use the "Inputs" attribute
|
|
||||||
internal static List<ActionStep> ConvertToSteps(
|
|
||||||
TemplateContext context,
|
|
||||||
TemplateToken steps)
|
|
||||||
{
|
|
||||||
var stepsSequence = steps.AssertSequence($"job {PipelineTemplateConstants.Steps}");
|
|
||||||
|
|
||||||
var result = new List<ActionStep>();
|
|
||||||
foreach (var stepsItem in stepsSequence)
|
|
||||||
{
|
|
||||||
var step = ConvertToStep(context, stepsItem);
|
|
||||||
if (step != null) // step = null means we are hitting error during step conversion, there should be an error in context.errors
|
|
||||||
{
|
|
||||||
if (step.Enabled)
|
|
||||||
{
|
|
||||||
result.Add(step);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ActionStep ConvertToStep(
|
|
||||||
TemplateContext context,
|
|
||||||
TemplateToken stepsItem)
|
|
||||||
{
|
|
||||||
var step = stepsItem.AssertMapping($"{PipelineTemplateConstants.Steps} item");
|
|
||||||
var continueOnError = default(ScalarToken);
|
|
||||||
var env = default(TemplateToken);
|
|
||||||
var id = default(StringToken);
|
|
||||||
var ifCondition = default(String);
|
|
||||||
var ifToken = default(ScalarToken);
|
|
||||||
var name = default(ScalarToken);
|
|
||||||
var run = default(ScalarToken);
|
|
||||||
var scope = default(StringToken);
|
|
||||||
var timeoutMinutes = default(ScalarToken);
|
|
||||||
var uses = default(StringToken);
|
|
||||||
var with = default(TemplateToken);
|
|
||||||
var workingDir = default(ScalarToken);
|
|
||||||
var path = default(ScalarToken);
|
|
||||||
var clean = default(ScalarToken);
|
|
||||||
var fetchDepth = default(ScalarToken);
|
|
||||||
var lfs = default(ScalarToken);
|
|
||||||
var submodules = default(ScalarToken);
|
|
||||||
var shell = default(ScalarToken);
|
|
||||||
|
|
||||||
foreach (var stepProperty in step)
|
|
||||||
{
|
|
||||||
var propertyName = stepProperty.Key.AssertString($"{PipelineTemplateConstants.Steps} item key");
|
|
||||||
|
|
||||||
switch (propertyName.Value)
|
|
||||||
{
|
|
||||||
case PipelineTemplateConstants.Clean:
|
|
||||||
clean = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Clean}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.ContinueOnError:
|
|
||||||
ConvertToStepContinueOnError(context, stepProperty.Value, allowExpressions: true); // Validate early if possible
|
|
||||||
continueOnError = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} {PipelineTemplateConstants.ContinueOnError}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.Env:
|
|
||||||
ConvertToStepEnvironment(context, stepProperty.Value, StringComparer.Ordinal, allowExpressions: true); // Validate early if possible
|
|
||||||
env = stepProperty.Value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.FetchDepth:
|
|
||||||
fetchDepth = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.FetchDepth}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.Id:
|
|
||||||
id = stepProperty.Value.AssertString($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Id}");
|
|
||||||
if (!NameValidation.IsValid(id.Value, true))
|
|
||||||
{
|
|
||||||
context.Error(id, $"Step id {id.Value} is invalid. Ids must start with a letter or '_' and contain only alphanumeric characters, '-', or '_'");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.If:
|
|
||||||
ifToken = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.If}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.Lfs:
|
|
||||||
lfs = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Lfs}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.Name:
|
|
||||||
name = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Name}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.Path:
|
|
||||||
path = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Path}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.Run:
|
|
||||||
run = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Run}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.Shell:
|
|
||||||
shell = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Shell}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.Scope:
|
|
||||||
scope = stepProperty.Value.AssertString($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Scope}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.Submodules:
|
|
||||||
submodules = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Submodules}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.TimeoutMinutes:
|
|
||||||
ConvertToStepTimeout(context, stepProperty.Value, allowExpressions: true); // Validate early if possible
|
|
||||||
timeoutMinutes = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.TimeoutMinutes}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.Uses:
|
|
||||||
uses = stepProperty.Value.AssertString($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Uses}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.With:
|
|
||||||
ConvertToStepInputs(context, stepProperty.Value, allowExpressions: true); // Validate early if possible
|
|
||||||
with = stepProperty.Value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PipelineTemplateConstants.WorkingDirectory:
|
|
||||||
workingDir = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.WorkingDirectory}");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Steps} item key"); // throws
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fixup the if-condition
|
|
||||||
var isDefaultScope = String.IsNullOrEmpty(scope?.Value);
|
|
||||||
ifCondition = ConvertToIfCondition(context, ifToken, false, isDefaultScope);
|
|
||||||
|
|
||||||
if (run != null)
|
|
||||||
{
|
|
||||||
var result = new ActionStep
|
|
||||||
{
|
|
||||||
ScopeName = scope?.Value,
|
|
||||||
ContextName = id?.Value,
|
|
||||||
ContinueOnError = continueOnError,
|
|
||||||
DisplayNameToken = name,
|
|
||||||
Condition = ifCondition,
|
|
||||||
TimeoutInMinutes = timeoutMinutes,
|
|
||||||
Environment = env,
|
|
||||||
Reference = new ScriptReference(),
|
|
||||||
};
|
|
||||||
|
|
||||||
var inputs = new MappingToken(null, null, null);
|
|
||||||
inputs.Add(new StringToken(null, null, null, PipelineConstants.ScriptStepInputs.Script), run);
|
|
||||||
|
|
||||||
if (workingDir != null)
|
|
||||||
{
|
|
||||||
inputs.Add(new StringToken(null, null, null, PipelineConstants.ScriptStepInputs.WorkingDirectory), workingDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shell != null)
|
|
||||||
{
|
|
||||||
inputs.Add(new StringToken(null, null, null, PipelineConstants.ScriptStepInputs.Shell), shell);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Inputs = inputs;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uses.AssertString($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Uses}");
|
|
||||||
var result = new ActionStep
|
|
||||||
{
|
|
||||||
ScopeName = scope?.Value,
|
|
||||||
ContextName = id?.Value,
|
|
||||||
ContinueOnError = continueOnError,
|
|
||||||
DisplayNameToken = name,
|
|
||||||
Condition = ifCondition,
|
|
||||||
TimeoutInMinutes = timeoutMinutes,
|
|
||||||
Inputs = with,
|
|
||||||
Environment = env,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (uses.Value.StartsWith("docker://", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
var image = uses.Value.Substring("docker://".Length);
|
|
||||||
result.Reference = new ContainerRegistryReference { Image = image };
|
|
||||||
}
|
|
||||||
else if (uses.Value.StartsWith("./") || uses.Value.StartsWith(".\\"))
|
|
||||||
{
|
|
||||||
result.Reference = new RepositoryPathReference
|
|
||||||
{
|
|
||||||
RepositoryType = PipelineConstants.SelfAlias,
|
|
||||||
Path = uses.Value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var usesSegments = uses.Value.Split('@');
|
|
||||||
var pathSegments = usesSegments[0].Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
var gitRef = usesSegments.Length == 2 ? usesSegments[1] : String.Empty;
|
|
||||||
|
|
||||||
if (usesSegments.Length != 2 ||
|
|
||||||
pathSegments.Length < 2 ||
|
|
||||||
String.IsNullOrEmpty(pathSegments[0]) ||
|
|
||||||
String.IsNullOrEmpty(pathSegments[1]) ||
|
|
||||||
String.IsNullOrEmpty(gitRef))
|
|
||||||
{
|
|
||||||
// todo: loc
|
|
||||||
context.Error(uses, $"Expected format {{org}}/{{repo}}[/path]@ref. Actual '{uses.Value}'");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var repositoryName = $"{pathSegments[0]}/{pathSegments[1]}";
|
|
||||||
var directoryPath = pathSegments.Length > 2 ? String.Join("/", pathSegments.Skip(2)) : String.Empty;
|
|
||||||
|
|
||||||
result.Reference = new RepositoryPathReference
|
|
||||||
{
|
|
||||||
RepositoryType = RepositoryTypes.GitHub,
|
|
||||||
Name = repositoryName,
|
|
||||||
Ref = gitRef,
|
|
||||||
Path = directoryPath,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When empty, default to "success()".
|
|
||||||
/// When a status function is not referenced, format as "success() && <CONDITION>".
|
|
||||||
/// </summary>
|
|
||||||
private static String ConvertToIfCondition(
|
|
||||||
TemplateContext context,
|
|
||||||
TemplateToken token,
|
|
||||||
Boolean isJob,
|
|
||||||
Boolean isDefaultScope)
|
|
||||||
{
|
|
||||||
String condition;
|
|
||||||
if (token is null)
|
|
||||||
{
|
|
||||||
condition = null;
|
|
||||||
}
|
|
||||||
else if (token is BasicExpressionToken expressionToken)
|
|
||||||
{
|
|
||||||
condition = expressionToken.Expression;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var stringToken = token.AssertString($"{(isJob ? "job" : "step")} {PipelineTemplateConstants.If}");
|
|
||||||
condition = stringToken.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (String.IsNullOrWhiteSpace(condition))
|
|
||||||
{
|
|
||||||
return $"{PipelineTemplateConstants.Success}()";
|
|
||||||
}
|
|
||||||
|
|
||||||
var expressionParser = new ExpressionParser();
|
|
||||||
var functions = default(IFunctionInfo[]);
|
|
||||||
var namedValues = default(INamedValueInfo[]);
|
|
||||||
if (isJob)
|
|
||||||
{
|
|
||||||
namedValues = s_jobIfNamedValues;
|
|
||||||
// TODO: refactor into seperate functions
|
|
||||||
// functions = PhaseCondition.FunctionInfo;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
namedValues = isDefaultScope ? s_stepNamedValues : s_stepInTemplateNamedValues;
|
|
||||||
functions = s_stepConditionFunctions;
|
|
||||||
}
|
|
||||||
|
|
||||||
var node = default(ExpressionNode);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
node = expressionParser.CreateTree(condition, null, namedValues, functions) as ExpressionNode;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
context.Error(token, ex);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node == null)
|
|
||||||
{
|
|
||||||
return $"{PipelineTemplateConstants.Success}()";
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasStatusFunction = node.Traverse().Any(x =>
|
|
||||||
{
|
|
||||||
if (x is Function function)
|
|
||||||
{
|
|
||||||
return String.Equals(function.Name, PipelineTemplateConstants.Always, StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
String.Equals(function.Name, PipelineTemplateConstants.Cancelled, StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
String.Equals(function.Name, PipelineTemplateConstants.Failure, StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
String.Equals(function.Name, PipelineTemplateConstants.Success, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
return hasStatusFunction ? condition : $"{PipelineTemplateConstants.Success}() && ({condition})";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly INamedValueInfo[] s_jobIfNamedValues = new INamedValueInfo[]
|
|
||||||
{
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.GitHub),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Needs),
|
|
||||||
};
|
|
||||||
private static readonly INamedValueInfo[] s_stepNamedValues = new INamedValueInfo[]
|
|
||||||
{
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Strategy),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Matrix),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Steps),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.GitHub),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Job),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Runner),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Env),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Needs),
|
|
||||||
};
|
|
||||||
private static readonly INamedValueInfo[] s_stepInTemplateNamedValues = new INamedValueInfo[]
|
|
||||||
{
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Strategy),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Matrix),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Steps),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Inputs),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.GitHub),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Job),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Runner),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Env),
|
|
||||||
new NamedValueInfo<NoOperationNamedValue>(PipelineTemplateConstants.Needs),
|
|
||||||
};
|
|
||||||
private static readonly IFunctionInfo[] s_stepConditionFunctions = new IFunctionInfo[]
|
|
||||||
{
|
|
||||||
new FunctionInfo<NoOperation>(PipelineTemplateConstants.Always, 0, 0),
|
|
||||||
new FunctionInfo<NoOperation>(PipelineTemplateConstants.Cancelled, 0, 0),
|
|
||||||
new FunctionInfo<NoOperation>(PipelineTemplateConstants.Failure, 0, 0),
|
|
||||||
new FunctionInfo<NoOperation>(PipelineTemplateConstants.Success, 0, 0),
|
|
||||||
new FunctionInfo<NoOperation>(PipelineTemplateConstants.HashFiles, 1, Byte.MaxValue),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,32 +159,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ActionStep> LoadCompositeSteps(
|
|
||||||
TemplateToken token
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var result = default(List<ActionStep>);
|
|
||||||
if (token != null && token.Type != TokenType.Null)
|
|
||||||
{
|
|
||||||
var context = CreateContext(null, null, setMissingContext: false);
|
|
||||||
// TODO: we might want to to have a bool to prevent it from filling in with missing context w/ dummy variables
|
|
||||||
try
|
|
||||||
{
|
|
||||||
token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.StepsInTemplate, token, 0, null, omitHeader: true);
|
|
||||||
context.Errors.Check();
|
|
||||||
result = PipelineTemplateConverter.ConvertToSteps(context, token);
|
|
||||||
}
|
|
||||||
catch (Exception ex) when (!(ex is TemplateValidationException))
|
|
||||||
{
|
|
||||||
context.Errors.Add(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Errors.Check();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public Dictionary<String, String> EvaluateStepEnvironment(
|
public Dictionary<String, String> EvaluateStepEnvironment(
|
||||||
TemplateToken token,
|
TemplateToken token,
|
||||||
DictionaryContextData contextData,
|
DictionaryContextData contextData,
|
||||||
@@ -426,8 +400,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
private TemplateContext CreateContext(
|
private TemplateContext CreateContext(
|
||||||
DictionaryContextData contextData,
|
DictionaryContextData contextData,
|
||||||
IList<IFunctionInfo> expressionFunctions,
|
IList<IFunctionInfo> expressionFunctions,
|
||||||
IEnumerable<KeyValuePair<String, Object>> expressionState = null,
|
IEnumerable<KeyValuePair<String, Object>> expressionState = null)
|
||||||
bool setMissingContext = true)
|
|
||||||
{
|
{
|
||||||
var result = new TemplateContext
|
var result = new TemplateContext
|
||||||
{
|
{
|
||||||
@@ -476,21 +449,18 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
|||||||
// - Evaluating early when all referenced contexts are available, even though all allowed
|
// - Evaluating early when all referenced contexts are available, even though all allowed
|
||||||
// contexts may not yet be available. For example, evaluating step display name can often
|
// contexts may not yet be available. For example, evaluating step display name can often
|
||||||
// be performed early.
|
// be performed early.
|
||||||
if (setMissingContext)
|
foreach (var name in s_expressionValueNames)
|
||||||
{
|
{
|
||||||
foreach (var name in s_expressionValueNames)
|
if (!result.ExpressionValues.ContainsKey(name))
|
||||||
{
|
{
|
||||||
if (!result.ExpressionValues.ContainsKey(name))
|
result.ExpressionValues[name] = null;
|
||||||
{
|
|
||||||
result.ExpressionValues[name] = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
foreach (var name in s_expressionFunctionNames)
|
}
|
||||||
|
foreach (var name in s_expressionFunctionNames)
|
||||||
|
{
|
||||||
|
if (!functionNames.Contains(name))
|
||||||
{
|
{
|
||||||
if (!functionNames.Contains(name))
|
result.ExpressionFunctions.Add(new FunctionInfo<NoOperation>(name, 0, Int32.MaxValue));
|
||||||
{
|
|
||||||
result.ExpressionFunctions.Add(new FunctionInfo<NoOperation>(name, 0, Int32.MaxValue));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -94,12 +94,5 @@ namespace GitHub.DistributedTask.Pipelines
|
|||||||
public static readonly String Resources = "resources";
|
public static readonly String Resources = "resources";
|
||||||
public static readonly String All = "all";
|
public static readonly String All = "all";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ScriptStepInputs
|
|
||||||
{
|
|
||||||
public static readonly String Script = "script";
|
|
||||||
public static readonly String WorkingDirectory = "workingDirectory";
|
|
||||||
public static readonly String Shell = "shell";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace GitHub.Services.OAuth
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the error description for the response.
|
/// Gets or sets the error description for the response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataMember(Name = "errordescription", EmitDefaultValue = false)]
|
[DataMember(Name = "error_description", EmitDefaultValue = false)]
|
||||||
public String ErrorDescription
|
public String ErrorDescription
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
|
|||||||
@@ -70,5 +70,24 @@ namespace GitHub.Runner.Common.Tests.Util
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Trait("Level", "L0")]
|
||||||
|
[Trait("Category", "Common")]
|
||||||
|
public void WhichHandleFullyQualifiedPath()
|
||||||
|
{
|
||||||
|
using (TestHostContext hc = new TestHostContext(this))
|
||||||
|
{
|
||||||
|
//Arrange
|
||||||
|
Tracing trace = hc.GetTrace();
|
||||||
|
|
||||||
|
// Act.
|
||||||
|
var gitPath = WhichUtil.Which("git", require: true, trace: trace);
|
||||||
|
var gitPath2 = WhichUtil.Which(gitPath, require: true, trace: trace);
|
||||||
|
|
||||||
|
// Assert.
|
||||||
|
Assert.Equal(gitPath, gitPath2);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -115,7 +115,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -154,7 +154,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -208,7 +208,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -287,7 +287,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -330,7 +330,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Step.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Step.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -361,7 +361,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -391,7 +391,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
{
|
{
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -417,7 +417,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object }));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object }));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -455,7 +455,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object }));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object }));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -493,7 +493,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object }));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object }));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -524,7 +524,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
@@ -560,7 +560,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
|
|
||||||
_ec.Object.Result = null;
|
_ec.Object.Result = null;
|
||||||
|
|
||||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||||
|
|
||||||
// Act.
|
// Act.
|
||||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||||
|
|||||||
Reference in New Issue
Block a user