mirror of
https://github.com/actions/runner.git
synced 2025-12-11 04:46:58 +00:00
Compare commits
8 Commits
releases/m
...
users/etha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bbed7d3c2 | ||
|
|
a4cc13e36f | ||
|
|
747addfca6 | ||
|
|
9d7bd4706b | ||
|
|
5822a38c39 | ||
|
|
d42c9da2d7 | ||
|
|
121deedeb5 | ||
|
|
a0942ed345 |
@@ -51,6 +51,19 @@ cd ./src
|
||||
./dev.(sh/cmd) test # run all unit tests before git commit/push
|
||||
```
|
||||
|
||||
View logs:
|
||||
```bash
|
||||
cd runner/_layout/_diag
|
||||
ls
|
||||
cat (Runner/Worker)_TIMESTAMP.log # view your log file
|
||||
```
|
||||
|
||||
Run Runner:
|
||||
```bash
|
||||
cd runner/_layout
|
||||
./run.sh # run your custom runner
|
||||
```
|
||||
|
||||
### Editors
|
||||
|
||||
[Using Visual Studio Code](https://code.visualstudio.com/)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
## Features
|
||||
- Resolve action download info from server (#508, #515, #550)
|
||||
- Print runner and machine name to log. (#539)
|
||||
- Allow runner to register against non-default runner groups (#517)
|
||||
## Bugs
|
||||
- Reduce input validation warnings (#506)
|
||||
- Fix null ref exception in SecretMasker caused by `hashfiles` timeout. (#516)
|
||||
@@ -9,7 +8,6 @@
|
||||
- Fix DataContract with Token service (#532)
|
||||
- Skip search $PATH on command with fully qualified path (#526)
|
||||
- Restore SELinux context on service file when SELinux is enabled (#525)
|
||||
- Fix trailing '.0' for Int64 values in ContextData (#572)
|
||||
## Misc
|
||||
- Remove SPS/Token migration code. Remove GHES url manipulate code. (#513)
|
||||
- Add sub-step for developer flow for clarity (#523)
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.267.2
|
||||
<Update to ./src/runnerversion when creating release>
|
||||
|
||||
2
src/Misc/dotnet-install.ps1
vendored
2
src/Misc/dotnet-install.ps1
vendored
@@ -885,4 +885,4 @@ exit 0
|
||||
# DsQOUWk8J6ojDurhrP536WI+3arg8PcnppHBLd/xNKYdlsTb+6qndgzKXkDDt1CV
|
||||
# 4zCyuZ7bO8eyZAmNoSZz22k7vus9UjBz/CDhXylo20N43nr29rWPItUgH4uvOGQn
|
||||
# t26Y/yjBaQImz32psrfJEMbQ7cl789s8WOx8
|
||||
# SIG # End signature block
|
||||
# SIG # End signature block
|
||||
@@ -90,7 +90,7 @@ namespace GitHub.Runner.Common
|
||||
public static readonly string Labels = "labels";
|
||||
public static readonly string MonitorSocketAddress = "monitorsocketaddress";
|
||||
public static readonly string Name = "name";
|
||||
public static readonly string RunnerGroup = "runnergroup";
|
||||
public static readonly string Pool = "pool";
|
||||
public static readonly string StartupType = "startuptype";
|
||||
public static readonly string Url = "url";
|
||||
public static readonly string UserName = "username";
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace GitHub.Runner.Listener
|
||||
Constants.Runner.CommandLine.Args.Labels,
|
||||
Constants.Runner.CommandLine.Args.MonitorSocketAddress,
|
||||
Constants.Runner.CommandLine.Args.Name,
|
||||
Constants.Runner.CommandLine.Args.RunnerGroup,
|
||||
Constants.Runner.CommandLine.Args.Pool,
|
||||
Constants.Runner.CommandLine.Args.StartupType,
|
||||
Constants.Runner.CommandLine.Args.Token,
|
||||
Constants.Runner.CommandLine.Args.Url,
|
||||
@@ -169,15 +169,6 @@ namespace GitHub.Runner.Listener
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetRunnerGroupName(string defaultPoolName = null)
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
name: Constants.Runner.CommandLine.Args.RunnerGroup,
|
||||
description: "Enter the name of the runner group to add this runner to:",
|
||||
defaultValue: defaultPoolName ?? "default",
|
||||
validator: Validators.NonEmptyValidator);
|
||||
}
|
||||
|
||||
public string GetToken()
|
||||
{
|
||||
return GetArgOrPrompt(
|
||||
|
||||
@@ -159,34 +159,17 @@ namespace GitHub.Runner.Listener.Configuration
|
||||
|
||||
_term.WriteSection("Runner Registration");
|
||||
|
||||
// If we have more than one runner group available, allow the user to specify which one to be added into
|
||||
string poolName = null;
|
||||
TaskAgentPool agentPool = null;
|
||||
//Get all the agent pools, and select the first private pool
|
||||
List<TaskAgentPool> agentPools = await _runnerServer.GetAgentPoolsAsync();
|
||||
TaskAgentPool defaultPool = agentPools?.Where(x => x.IsInternal).FirstOrDefault();
|
||||
TaskAgentPool agentPool = agentPools?.Where(x => x.IsHosted == false).FirstOrDefault();
|
||||
|
||||
if (agentPools?.Where(x => !x.IsHosted).Count() > 1)
|
||||
if (agentPool == null)
|
||||
{
|
||||
poolName = command.GetRunnerGroupName(defaultPool?.Name);
|
||||
_term.WriteLine();
|
||||
agentPool = agentPools.Where(x => string.Equals(poolName, x.Name, StringComparison.OrdinalIgnoreCase) && !x.IsHosted).FirstOrDefault();
|
||||
throw new TaskAgentPoolNotFoundException($"Could not find any private pool. Contact support.");
|
||||
}
|
||||
else
|
||||
{
|
||||
agentPool = defaultPool;
|
||||
}
|
||||
|
||||
if (agentPool == null && poolName == null)
|
||||
{
|
||||
throw new TaskAgentPoolNotFoundException($"Could not find any self-hosted runner groups. Contact support.");
|
||||
}
|
||||
else if (agentPool == null && poolName != null)
|
||||
{
|
||||
throw new TaskAgentPoolNotFoundException($"Could not find any self-hosted runner group named \"{poolName}\".");
|
||||
}
|
||||
else
|
||||
{
|
||||
Trace.Info("Found a self-hosted runner group with id {1} and name {2}", agentPool.Id, agentPool.Name);
|
||||
Trace.Info("Found a private pool with id {1} and name {2}", agentPool.Id, agentPool.Name);
|
||||
runnerSettings.PoolId = agentPool.Id;
|
||||
runnerSettings.PoolName = agentPool.Name;
|
||||
}
|
||||
|
||||
@@ -395,6 +395,12 @@ namespace GitHub.Runner.Worker
|
||||
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
|
||||
{
|
||||
throw new NotSupportedException(definition.Data.Execution.ExecutionType.ToString());
|
||||
@@ -1038,6 +1044,11 @@ namespace GitHub.Runner.Worker
|
||||
Trace.Info($"Action plugin: {(actionDefinitionData.Execution as PluginActionExecutionData).Plugin}, no more preparation.");
|
||||
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
|
||||
{
|
||||
throw new NotSupportedException(actionDefinitionData.Execution.ExecutionType.ToString());
|
||||
@@ -1148,6 +1159,7 @@ namespace GitHub.Runner.Worker
|
||||
NodeJS,
|
||||
Plugin,
|
||||
Script,
|
||||
Composite,
|
||||
}
|
||||
|
||||
public sealed class ContainerActionExecutionData : ActionExecutionData
|
||||
@@ -1204,6 +1216,14 @@ namespace GitHub.Runner.Worker
|
||||
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
|
||||
{
|
||||
private string _initCondition = $"{Constants.Expressions.Always}()";
|
||||
|
||||
@@ -14,6 +14,7 @@ using YamlDotNet.Core;
|
||||
using YamlDotNet.Core.Events;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Pipelines = GitHub.DistributedTask.Pipelines;
|
||||
|
||||
namespace GitHub.Runner.Worker
|
||||
{
|
||||
@@ -32,8 +33,6 @@ namespace GitHub.Runner.Worker
|
||||
public sealed class ActionManifestManager : RunnerService, IActionManifestManager
|
||||
{
|
||||
private TemplateSchema _actionManifestSchema;
|
||||
private IReadOnlyList<String> _fileTable;
|
||||
|
||||
public override void Initialize(IHostContext hostContext)
|
||||
{
|
||||
base.Initialize(hostContext);
|
||||
@@ -54,22 +53,39 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public ActionDefinitionData Load(IExecutionContext executionContext, string manifestFile)
|
||||
{
|
||||
var context = CreateContext(executionContext);
|
||||
var templateContext = CreateContext(executionContext);
|
||||
ActionDefinitionData actionDefinition = new ActionDefinitionData();
|
||||
|
||||
// Clean up file name real quick
|
||||
// Instead of using Regex which can be computationally expensive,
|
||||
// we can just remove the # of characters from the fileName according to the length of the basePath
|
||||
string basePath = HostContext.GetDirectory(WellKnownDirectory.Actions);
|
||||
string fileRelativePath = manifestFile;
|
||||
if (manifestFile.Contains(basePath))
|
||||
{
|
||||
fileRelativePath = manifestFile.Remove(0, basePath.Length + 1);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var token = default(TemplateToken);
|
||||
|
||||
// Get the file ID
|
||||
var fileId = context.GetFileId(manifestFile);
|
||||
_fileTable = context.GetFileTable();
|
||||
var fileId = templateContext.GetFileId(fileRelativePath);
|
||||
|
||||
// Add this file to the FileTable in executionContext if it hasn't been added already
|
||||
// we use > since fileID is 1 indexed
|
||||
if (fileId > executionContext.FileTable.Count)
|
||||
{
|
||||
executionContext.FileTable.Add(fileRelativePath);
|
||||
}
|
||||
|
||||
// Read the file
|
||||
var fileContent = File.ReadAllText(manifestFile);
|
||||
using (var stringReader = new StringReader(fileContent))
|
||||
{
|
||||
var yamlObjectReader = new YamlObjectReader(null, stringReader);
|
||||
token = TemplateReader.Read(context, "action-root", yamlObjectReader, fileId, out _);
|
||||
var yamlObjectReader = new YamlObjectReader(fileId, stringReader);
|
||||
token = TemplateReader.Read(templateContext, "action-root", yamlObjectReader, fileId, out _);
|
||||
}
|
||||
|
||||
var actionMapping = token.AssertMapping("action manifest root");
|
||||
@@ -88,11 +104,11 @@ namespace GitHub.Runner.Worker
|
||||
break;
|
||||
|
||||
case "inputs":
|
||||
ConvertInputs(context, actionPair.Value, actionDefinition);
|
||||
ConvertInputs(templateContext, actionPair.Value, actionDefinition);
|
||||
break;
|
||||
|
||||
case "runs":
|
||||
actionDefinition.Execution = ConvertRuns(context, actionPair.Value);
|
||||
actionDefinition.Execution = ConvertRuns(executionContext, templateContext, actionPair.Value);
|
||||
break;
|
||||
default:
|
||||
Trace.Info($"Ignore action property {propertyName}.");
|
||||
@@ -103,24 +119,24 @@ namespace GitHub.Runner.Worker
|
||||
catch (Exception ex)
|
||||
{
|
||||
Trace.Error(ex);
|
||||
context.Errors.Add(ex);
|
||||
templateContext.Errors.Add(ex);
|
||||
}
|
||||
|
||||
if (context.Errors.Count > 0)
|
||||
if (templateContext.Errors.Count > 0)
|
||||
{
|
||||
foreach (var error in context.Errors)
|
||||
foreach (var error in templateContext.Errors)
|
||||
{
|
||||
Trace.Error($"Action.yml load error: {error.Message}");
|
||||
executionContext.Error(error.Message);
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Fail to load {manifestFile}");
|
||||
throw new ArgumentException($"Fail to load {fileRelativePath}");
|
||||
}
|
||||
|
||||
if (actionDefinition.Execution == null)
|
||||
{
|
||||
executionContext.Debug($"Loaded action.yml file: {StringUtil.ConvertToJson(actionDefinition)}");
|
||||
throw new ArgumentException($"Top level 'runs:' section is required for {manifestFile}");
|
||||
throw new ArgumentException($"Top level 'runs:' section is required for {fileRelativePath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -281,19 +297,17 @@ namespace GitHub.Runner.Worker
|
||||
result.ExpressionFunctions.Add(item);
|
||||
}
|
||||
|
||||
// Add the file table
|
||||
if (_fileTable?.Count > 0)
|
||||
// Add the file table from the Execution Context
|
||||
for (var i = 0; i < executionContext.FileTable.Count; i++)
|
||||
{
|
||||
for (var i = 0 ; i < _fileTable.Count ; i++)
|
||||
{
|
||||
result.GetFileId(_fileTable[i]);
|
||||
}
|
||||
result.GetFileId(executionContext.FileTable[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private ActionExecutionData ConvertRuns(
|
||||
IExecutionContext executionContext,
|
||||
TemplateContext context,
|
||||
TemplateToken inputsToken)
|
||||
{
|
||||
@@ -311,6 +325,8 @@ namespace GitHub.Runner.Worker
|
||||
var postToken = default(StringToken);
|
||||
var postEntrypointToken = default(StringToken);
|
||||
var postIfToken = default(StringToken);
|
||||
var stepsLoaded = default(List<Pipelines.Step>);
|
||||
|
||||
foreach (var run in runsMapping)
|
||||
{
|
||||
var runsKey = run.Key.AssertString("runs key").Value;
|
||||
@@ -355,6 +371,16 @@ namespace GitHub.Runner.Worker
|
||||
case "pre-if":
|
||||
preIfToken = run.Value.AssertString("pre-if");
|
||||
break;
|
||||
case "steps":
|
||||
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||
{
|
||||
var steps = run.Value.AssertSequence("steps");
|
||||
var evaluator = executionContext.ToPipelineTemplateEvaluator();
|
||||
// TODO: Change this so that we process each type of step
|
||||
stepsLoaded = evaluator.LoadCompositeSteps(steps);
|
||||
break;
|
||||
}
|
||||
throw new Exception("You aren't supposed to be using Composite Actions yet!");
|
||||
default:
|
||||
Trace.Info($"Ignore run property {runsKey}.");
|
||||
break;
|
||||
@@ -402,6 +428,21 @@ 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
|
||||
{
|
||||
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; }
|
||||
|
||||
// Only job level ExecutionContext has JobSteps
|
||||
Queue<IStep> JobSteps { get; }
|
||||
List<IStep> JobSteps { get; }
|
||||
|
||||
// Only job level ExecutionContext has PostJobSteps
|
||||
Stack<IStep> PostJobSteps { get; }
|
||||
@@ -105,6 +105,7 @@ namespace GitHub.Runner.Worker
|
||||
// others
|
||||
void ForceTaskComplete();
|
||||
void RegisterPostJobStep(IStep step);
|
||||
void RegisterNestedStep(IStep step, DictionaryContextData inputsData, int location, Dictionary<string, string> envData);
|
||||
}
|
||||
|
||||
public sealed class ExecutionContext : RunnerService, IExecutionContext
|
||||
@@ -159,7 +160,7 @@ namespace GitHub.Runner.Worker
|
||||
public List<ContainerInfo> ServiceContainers { get; private set; }
|
||||
|
||||
// Only job level ExecutionContext has JobSteps
|
||||
public Queue<IStep> JobSteps { get; private set; }
|
||||
public List<IStep> JobSteps { get; private set; }
|
||||
|
||||
// Only job level ExecutionContext has PostJobSteps
|
||||
public Stack<IStep> PostJobSteps { get; private set; }
|
||||
@@ -169,7 +170,6 @@ namespace GitHub.Runner.Worker
|
||||
|
||||
public bool EchoOnActionCommand { get; set; }
|
||||
|
||||
|
||||
public TaskResult? Result
|
||||
{
|
||||
get
|
||||
@@ -266,6 +266,35 @@ namespace GitHub.Runner.Worker
|
||||
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, Dictionary<string, string> envData)
|
||||
{
|
||||
// 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;
|
||||
|
||||
// Add the composite action environment variables to each step.
|
||||
// If the key already exists, we override it since the composite action env variables will have higher precedence
|
||||
// Note that for each composite action step, it's environment variables will be set in the StepRunner automatically
|
||||
// step.ExecutionContext.SetEnvironmentVariables(envData);
|
||||
#if OS_WINDOWS
|
||||
var envContext = new DictionaryContextData();
|
||||
#else
|
||||
var envContext = new CaseSensitiveDictionaryContextData();
|
||||
#endif
|
||||
foreach (var pair in envData)
|
||||
{
|
||||
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
||||
}
|
||||
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||
|
||||
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)
|
||||
{
|
||||
Trace.Entering();
|
||||
@@ -660,7 +689,7 @@ namespace GitHub.Runner.Worker
|
||||
PrependPath = new List<string>();
|
||||
|
||||
// JobSteps for job ExecutionContext
|
||||
JobSteps = new Queue<IStep>();
|
||||
JobSteps = new List<IStep>();
|
||||
|
||||
// PostJobSteps for job ExecutionContext
|
||||
PostJobSteps = new Stack<IStep>();
|
||||
|
||||
141
src/Runner.Worker/Handlers/CompositeActionHandler.cs
Normal file
141
src/Runner.Worker/Handlers/CompositeActionHandler.cs
Normal file
@@ -0,0 +1,141 @@
|
||||
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);
|
||||
|
||||
// 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;
|
||||
|
||||
// Resolve action steps
|
||||
var compositeSteps = Data.Steps;
|
||||
|
||||
// TODO: Assume that each step is not an actionStep
|
||||
// How do we handle all types of steps?????
|
||||
|
||||
// While loop till we have reached the last layer?
|
||||
List<Pipelines.Step> stepsToAppend = new List<Pipelines.Step>();
|
||||
|
||||
// First put each step in stepsToAppend
|
||||
foreach (var step in compositeSteps)
|
||||
{
|
||||
stepsToAppend.Append(step);
|
||||
}
|
||||
|
||||
// We go through each step and push to the top of the stack its children.
|
||||
// That way, we go through each steps, steps of steps in order
|
||||
// This is an ITERATIVE approach. While a recursive approach may be more elegant,
|
||||
// that would use a lot more memory in the call stack.
|
||||
// Ex:
|
||||
// Let's say we have 4 composite steps with the first step that has 3 children
|
||||
// A (composite step)=> a1, a2, a3
|
||||
// B (non composite)
|
||||
// C (non composite)
|
||||
// D (non composite)
|
||||
// It would be executed in this order => a1, a2, a3, A (steps within A), B, C, D
|
||||
while (stepsToAppend != null)
|
||||
{
|
||||
var currentStep = stepsToAppend[0];
|
||||
|
||||
// TODO: Create another StepsContext?
|
||||
// In the original StepsRunner, we could lock the thread and only proceed after we finish processing these steps
|
||||
// Then, we invoke the CompositeStepsRunner class?
|
||||
|
||||
// TODO: We have to create another Execution Context for Composite Actions
|
||||
// See below
|
||||
|
||||
// TODO: Append to StepsRunner
|
||||
// by invoking a RegisterNestedStep on the Composite Action Exeuction Context for Composite Action Steps
|
||||
|
||||
|
||||
}
|
||||
|
||||
// foreach (Pipelines.Step 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.
|
||||
|
||||
// // TODO: how are we going to order each step?
|
||||
// // How is this going to look in the UI (will we have a bunch of nesting)
|
||||
// // ^ We need to focus on how we are going to get the steps to run in the right order.
|
||||
|
||||
// var actionRunner = HostContext.CreateService<IActionRunner>();
|
||||
// actionRunner.Action = aStep;
|
||||
// actionRunner.Stage = stage;
|
||||
// actionRunner.Condition = aStep.Condition;
|
||||
// actionRunner.DisplayName = aStep.DisplayName;
|
||||
|
||||
// ExecutionContext.RegisterNestedStep(actionRunner, inputsData, location, Environment);
|
||||
// location++;
|
||||
// }
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,11 @@ namespace GitHub.Runner.Worker.Handlers
|
||||
handler = HostContext.CreateService<IRunnerPluginHandler>();
|
||||
(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
|
||||
{
|
||||
// This should never happen.
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace GitHub.Runner.Worker
|
||||
{
|
||||
foreach (var step in jobSteps)
|
||||
{
|
||||
jobContext.JobSteps.Enqueue(step);
|
||||
jobContext.JobSteps.Add(step);
|
||||
}
|
||||
|
||||
await stepsRunner.RunAsync(jobContext);
|
||||
|
||||
@@ -59,14 +59,15 @@ namespace GitHub.Runner.Worker
|
||||
checkPostJobActions = true;
|
||||
while (jobContext.PostJobSteps.TryPop(out var postStep))
|
||||
{
|
||||
jobContext.JobSteps.Enqueue(postStep);
|
||||
jobContext.JobSteps.Add(postStep);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var step = jobContext.JobSteps.Dequeue();
|
||||
var nextStep = jobContext.JobSteps.Count > 0 ? jobContext.JobSteps.Peek() : null;
|
||||
var step = jobContext.JobSteps[0];
|
||||
jobContext.JobSteps.RemoveAt(0);
|
||||
var nextStep = jobContext.JobSteps.Count > 0 ? jobContext.JobSteps[0] : null;
|
||||
|
||||
Trace.Info($"Processing step: DisplayName='{step.DisplayName}'");
|
||||
ArgUtil.NotNull(step.ExecutionContext, nameof(step.ExecutionContext));
|
||||
@@ -92,12 +93,28 @@ namespace GitHub.Runner.Worker
|
||||
#else
|
||||
var envContext = new CaseSensitiveDictionaryContextData();
|
||||
#endif
|
||||
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||
// Global env
|
||||
foreach (var pair in step.ExecutionContext.EnvironmentVariables)
|
||||
{
|
||||
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
|
||||
}
|
||||
|
||||
// Stomps over with outside step env
|
||||
if (step.ExecutionContext.ExpressionValues.TryGetValue("env", out var envContextData))
|
||||
{
|
||||
#if OS_WINDOWS
|
||||
var dict = envContextData as DictionaryContextData;
|
||||
#else
|
||||
var dict = envContextData as CaseSensitiveDictionaryContextData;
|
||||
#endif
|
||||
foreach (var pair in dict)
|
||||
{
|
||||
envContext[pair.Key] = pair.Value;
|
||||
}
|
||||
}
|
||||
|
||||
step.ExecutionContext.ExpressionValues["env"] = envContext;
|
||||
|
||||
bool evaluateStepEnvFailed = false;
|
||||
if (step is IActionRunner actionStep)
|
||||
{
|
||||
@@ -409,7 +426,11 @@ namespace GitHub.Runner.Worker
|
||||
scope = scopesToInitialize.Pop();
|
||||
executionContext.Debug($"Initializing scope '{scope.Name}'");
|
||||
executionContext.ExpressionValues["steps"] = stepsContext.GetScope(scope.ParentName);
|
||||
executionContext.ExpressionValues["inputs"] = !String.IsNullOrEmpty(scope.ParentName) ? scopeInputs[scope.ParentName] : null;
|
||||
// TODO: Fix this temporary workaround for Composite Actions
|
||||
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 inputs = default(DictionaryContextData);
|
||||
try
|
||||
@@ -432,7 +453,11 @@ namespace GitHub.Runner.Worker
|
||||
// Setup expression values
|
||||
var scopeName = executionContext.ScopeName;
|
||||
executionContext.ExpressionValues["steps"] = stepsContext.GetScope(scopeName);
|
||||
executionContext.ExpressionValues["inputs"] = string.IsNullOrEmpty(scopeName) ? null : scopeInputs[scopeName];
|
||||
// TODO: Fix this temporary workaround for Composite Actions
|
||||
if (!executionContext.ExpressionValues.ContainsKey("inputs") && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
|
||||
{
|
||||
executionContext.ExpressionValues["inputs"] = string.IsNullOrEmpty(scopeName) ? null : scopeInputs[scopeName];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
"one-of": [
|
||||
"container-runs",
|
||||
"node12-runs",
|
||||
"plugin-runs"
|
||||
"plugin-runs",
|
||||
"composite-runs"
|
||||
]
|
||||
},
|
||||
"container-runs": {
|
||||
@@ -83,6 +84,36 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"context": [
|
||||
"inputs"
|
||||
|
||||
@@ -65,6 +65,7 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
public const String StepEnv = "step-env";
|
||||
public const String StepIfResult = "step-if-result";
|
||||
public const String Steps = "steps";
|
||||
public const String StepsInTemplate = "steps-in-template";
|
||||
public const String StepsScopeInputs = "steps-scope-inputs";
|
||||
public const String StepsScopeOutputs = "steps-scope-outputs";
|
||||
public const String StepsTemplateRoot = "steps-template-root";
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
var evaluationResult = EvaluationResult.CreateIntermediateResult(null, ifResult);
|
||||
return evaluationResult.IsTruthy;
|
||||
}
|
||||
|
||||
internal static Boolean? ConvertToStepContinueOnError(
|
||||
TemplateContext context,
|
||||
TemplateToken token,
|
||||
@@ -264,5 +263,350 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static List<Step> ConvertToSteps(
|
||||
TemplateContext context,
|
||||
TemplateToken steps)
|
||||
{
|
||||
var stepsSequence = steps.AssertSequence($"job {PipelineTemplateConstants.Steps}");
|
||||
|
||||
var result = new List<Step>();
|
||||
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 Step 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,6 +159,32 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO: Change to return a variety of steps.
|
||||
public List<Step> LoadCompositeSteps(
|
||||
TemplateToken token)
|
||||
{
|
||||
var result = default(List<Step>);
|
||||
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(
|
||||
TemplateToken token,
|
||||
DictionaryContextData contextData,
|
||||
@@ -400,7 +426,8 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
private TemplateContext CreateContext(
|
||||
DictionaryContextData contextData,
|
||||
IList<IFunctionInfo> expressionFunctions,
|
||||
IEnumerable<KeyValuePair<String, Object>> expressionState = null)
|
||||
IEnumerable<KeyValuePair<String, Object>> expressionState = null,
|
||||
bool setMissingContext = true)
|
||||
{
|
||||
var result = new TemplateContext
|
||||
{
|
||||
@@ -449,18 +476,21 @@ namespace GitHub.DistributedTask.Pipelines.ObjectTemplating
|
||||
// - 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
|
||||
// be performed early.
|
||||
foreach (var name in s_expressionValueNames)
|
||||
if (setMissingContext)
|
||||
{
|
||||
if (!result.ExpressionValues.ContainsKey(name))
|
||||
foreach (var name in s_expressionValueNames)
|
||||
{
|
||||
result.ExpressionValues[name] = null;
|
||||
if (!result.ExpressionValues.ContainsKey(name))
|
||||
{
|
||||
result.ExpressionValues[name] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (var name in s_expressionFunctionNames)
|
||||
{
|
||||
if (!functionNames.Contains(name))
|
||||
foreach (var name in s_expressionFunctionNames)
|
||||
{
|
||||
result.ExpressionFunctions.Add(new FunctionInfo<NoOperation>(name, 0, Int32.MaxValue));
|
||||
if (!functionNames.Contains(name))
|
||||
{
|
||||
result.ExpressionFunctions.Add(new FunctionInfo<NoOperation>(name, 0, Int32.MaxValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,5 +94,12 @@ namespace GitHub.DistributedTask.Pipelines
|
||||
public static readonly String Resources = "resources";
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ namespace GitHub.DistributedTask.WebApi
|
||||
this.PoolType = referenceToBeCloned.PoolType;
|
||||
this.Size = referenceToBeCloned.Size;
|
||||
this.IsLegacy = referenceToBeCloned.IsLegacy;
|
||||
this.IsInternal = referenceToBeCloned.IsInternal;
|
||||
}
|
||||
|
||||
public TaskAgentPoolReference Clone()
|
||||
@@ -68,16 +67,6 @@ namespace GitHub.DistributedTask.WebApi
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not this pool is internal and can't be modified by users
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public bool IsInternal
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the pool
|
||||
/// </summary>
|
||||
|
||||
@@ -39,12 +39,10 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
private string _expectedToken = "expectedToken";
|
||||
private string _expectedServerUrl = "https://codedev.ms";
|
||||
private string _expectedAgentName = "expectedAgentName";
|
||||
private string _defaultRunnerGroupName = "defaultRunnerGroup";
|
||||
private string _secondRunnerGroupName = "secondRunnerGroup";
|
||||
private string _expectedPoolName = "poolName";
|
||||
private string _expectedAuthType = "pat";
|
||||
private string _expectedWorkFolder = "_work";
|
||||
private int _defaultRunnerGroupId = 1;
|
||||
private int _secondRunnerGroupId = 2;
|
||||
private int _expectedPoolId = 1;
|
||||
private RSACryptoServiceProvider rsa = null;
|
||||
private RunnerSettings _configMgrAgentSettings = new RunnerSettings();
|
||||
|
||||
@@ -99,7 +97,7 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
_serviceControlManager.Setup(x => x.GenerateScripts(It.IsAny<RunnerSettings>()));
|
||||
#endif
|
||||
|
||||
var expectedPools = new List<TaskAgentPool>() { new TaskAgentPool(_defaultRunnerGroupName) { Id = _defaultRunnerGroupId, IsInternal = true }, new TaskAgentPool(_secondRunnerGroupName) { Id = _secondRunnerGroupId } };
|
||||
var expectedPools = new List<TaskAgentPool>() { new TaskAgentPool(_expectedPoolName) { Id = _expectedPoolId } };
|
||||
_runnerServer.Setup(x => x.GetAgentPoolsAsync(It.IsAny<string>(), It.IsAny<TaskAgentPoolType>())).Returns(Task.FromResult(expectedPools));
|
||||
|
||||
var expectedAgents = new List<TaskAgent>();
|
||||
@@ -157,7 +155,7 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
"configure",
|
||||
"--url", _expectedServerUrl,
|
||||
"--name", _expectedAgentName,
|
||||
"--runnergroup", _secondRunnerGroupName,
|
||||
"--pool", _expectedPoolName,
|
||||
"--work", _expectedWorkFolder,
|
||||
"--auth", _expectedAuthType,
|
||||
"--token", _expectedToken,
|
||||
@@ -177,7 +175,7 @@ namespace GitHub.Runner.Common.Tests.Listener.Configuration
|
||||
Assert.NotNull(s);
|
||||
Assert.True(s.ServerUrl.Equals(_expectedServerUrl));
|
||||
Assert.True(s.AgentName.Equals(_expectedAgentName));
|
||||
Assert.True(s.PoolId.Equals(_secondRunnerGroupId));
|
||||
Assert.True(s.PoolId.Equals(_expectedPoolId));
|
||||
Assert.True(s.WorkFolder.Equals(_expectedWorkFolder));
|
||||
|
||||
// validate GetAgentPoolsAsync gets called twice with automation pool type
|
||||
|
||||
@@ -3584,6 +3584,7 @@ runs:
|
||||
_ec.Setup(x => x.Variables).Returns(new Variables(_hc, variables));
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
_ec.Setup(x => x.FileTable).Returns(new List<String>());
|
||||
_ec.Setup(x => x.Plan).Returns(new TaskOrchestrationPlanReference());
|
||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); });
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||
|
||||
@@ -759,6 +759,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_ec.Setup(x => x.Variables).Returns(new Variables(_hc, new Dictionary<string, VariableValue>()));
|
||||
_ec.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
_ec.Setup(x => x.FileTable).Returns(new List<String>());
|
||||
_ec.Setup(x => x.Write(It.IsAny<string>(), It.IsAny<string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"{tag}{message}"); });
|
||||
_ec.Setup(x => x.AddIssue(It.IsAny<Issue>(), It.IsAny<string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); });
|
||||
}
|
||||
|
||||
@@ -379,6 +379,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
_ec.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
_ec.Setup(x => x.IntraActionState).Returns(new Dictionary<string, string>());
|
||||
_ec.Setup(x => x.EnvironmentVariables).Returns(new Dictionary<string, string>());
|
||||
_ec.Setup(x => x.FileTable).Returns(new List<String>());
|
||||
_ec.Setup(x => x.SetGitHubContext(It.IsAny<string>(), It.IsAny<string>()));
|
||||
_ec.Setup(x => x.GetGitHubContext(It.IsAny<string>())).Returns("{\"foo\":\"bar\"}");
|
||||
_ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token);
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -115,7 +115,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -154,7 +154,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -208,7 +208,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -287,7 +287,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Steps.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -330,7 +330,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Step.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Step.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -361,7 +361,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -391,7 +391,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
{
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(variableSet.Select(x => x.Object).ToList()));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -417,7 +417,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object }));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -426,11 +426,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
||||
|
||||
#if OS_WINDOWS
|
||||
Assert.Equal("100", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("100"));
|
||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env2"].AssertString("github_actions"));
|
||||
Assert.Equal("100", step1.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("100"));
|
||||
Assert.Equal("github_actions", step1.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env2"].AssertString("github_actions"));
|
||||
#else
|
||||
Assert.Equal("100", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("100"));
|
||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env2"].AssertString("github_actions"));
|
||||
Assert.Equal("100", step1.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("100"));
|
||||
Assert.Equal("github_actions", step1.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env2"].AssertString("github_actions"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -455,7 +455,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object }));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -463,13 +463,13 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
// Assert.
|
||||
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
||||
#if OS_WINDOWS
|
||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env3"].AssertString("github_actions"));
|
||||
Assert.False(_ec.Object.ExpressionValues["env"].AssertDictionary("env").ContainsKey("env2"));
|
||||
Assert.Equal("1000", step2.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("github_actions", step2.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env3"].AssertString("github_actions"));
|
||||
Assert.False(step2.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env").ContainsKey("env2"));
|
||||
#else
|
||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("github_actions", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env3"].AssertString("github_actions"));
|
||||
Assert.False(_ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env").ContainsKey("env2"));
|
||||
Assert.Equal("1000", step2.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("github_actions", step2.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env3"].AssertString("github_actions"));
|
||||
Assert.False(step2.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env").ContainsKey("env2"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -493,7 +493,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object }));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -501,11 +501,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
// Assert.
|
||||
Assert.Equal(TaskResult.Succeeded, _ec.Object.Result ?? TaskResult.Succeeded);
|
||||
#if OS_WINDOWS
|
||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("something", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env2"].AssertString("something"));
|
||||
Assert.Equal("1000", step2.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("something", step2.Object.ExecutionContext.ExpressionValues["env"].AssertDictionary("env")["env2"].AssertString("something"));
|
||||
#else
|
||||
Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("something", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env2"].AssertString("something"));
|
||||
Assert.Equal("1000", step2.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("1000"));
|
||||
Assert.Equal("something", step2.Object.ExecutionContext.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env2"].AssertString("something"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -524,7 +524,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -560,7 +560,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
|
||||
_ec.Object.Result = null;
|
||||
|
||||
_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||
_ec.Setup(x => x.JobSteps).Returns(new List<IStep>(new[] { step1.Object, step2.Object, step3.Object }));
|
||||
|
||||
// Act.
|
||||
await _stepsRunner.RunAsync(jobContext: _ec.Object);
|
||||
@@ -602,7 +602,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
||||
stepContext.Setup(x => x.WriteDebug).Returns(true);
|
||||
stepContext.Setup(x => x.Variables).Returns(_variables);
|
||||
stepContext.Setup(x => x.EnvironmentVariables).Returns(_env);
|
||||
stepContext.Setup(x => x.ExpressionValues).Returns(_contexts);
|
||||
stepContext.Setup(x => x.ExpressionValues).Returns(new DictionaryContextData());
|
||||
stepContext.Setup(x => x.ExpressionFunctions).Returns(new List<IFunctionInfo>());
|
||||
stepContext.Setup(x => x.JobContext).Returns(_jobContext);
|
||||
stepContext.Setup(x => x.StepsContext).Returns(_stepContext);
|
||||
|
||||
@@ -1 +1 @@
|
||||
2.267.2
|
||||
2.267.0
|
||||
|
||||
Reference in New Issue
Block a user