Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composite Actions UI #578

Merged
merged 121 commits into from
Jul 13, 2020
Merged
Show file tree
Hide file tree
Changes from 114 commits
Commits
Show all changes
121 commits
Select commit Hold shift + click to select a range
038e5e2
Composite Action Run Steps
ethanchewy Jun 18, 2020
e56b243
Env Flow => Able to get env variables and overwrite current env varia…
ethanchewy Jun 18, 2020
6552263
clean up
ethanchewy Jun 18, 2020
180a687
Clean up trace messages + add Trace debug in ActionManager
ethanchewy Jun 18, 2020
66cadeb
Merge branch 'users/ethanchewy/compositetest2' of https://github.com/…
ethanchewy Jun 18, 2020
96e0037
Add debugging message
ethanchewy Jun 18, 2020
496064f
Optimize runtime of code
ethanchewy Jun 18, 2020
5843362
Change String to string
ethanchewy Jun 19, 2020
941a24e
Add comma to Composite
ethanchewy Jun 19, 2020
28be3df
Merge branch 'users/ethanchewy/compositetest2' of https://github.com/…
ethanchewy Jun 19, 2020
9939cf5
Change JobSteps to a List, Change Register Step function name
ethanchewy Jun 19, 2020
5988076
Add TODO, remove unn. content
ethanchewy Jun 19, 2020
00a736d
Remove unnecessary code
ethanchewy Jun 19, 2020
e4dfd0e
Fix unit tests
ethanchewy Jun 19, 2020
f8054f9
Add/Merge changes from Multiple Steps PR
ethanchewy Jun 19, 2020
45ddd42
Fix env format
ethanchewy Jun 20, 2020
37849dc
Remove comment
ethanchewy Jun 22, 2020
94e7b47
Remove TODO message for context
ethanchewy Jun 22, 2020
a254442
Add verbose trace logs which are only viewable by devs
ethanchewy Jun 22, 2020
fbef557
Merge branch 'users/ethanchewy/compositetest2' of https://github.com/…
ethanchewy Jun 22, 2020
43a3006
Initial Start for FileTable stuff
ethanchewy Jun 22, 2020
58d11ef
Progress towards passing FileTable or FileID or FileName
ethanchewy Jun 22, 2020
0d5e84b
Sort usings in Composite Action Handler
ethanchewy Jun 23, 2020
9ec7047
Change 0 to location
ethanchewy Jun 23, 2020
8aadbbd
Update context variables in composite action yaml
ethanchewy Jun 23, 2020
f9b28c7
Add helpful error message for null steps
ethanchewy Jun 23, 2020
da2da85
Pass fileID to all children token of root action token
ethanchewy Jun 23, 2020
ba4ce9c
Change confusing term context => templateContext, Eliminate _fileTabl…
ethanchewy Jun 23, 2020
9c60f1a
Merge branch 'users/ethanchewy/compositetest2' of https://github.com/…
ethanchewy Jun 23, 2020
368b625
Remove unnessary FileID attribute from CompositeActionExecutionData
ethanchewy Jun 23, 2020
0a6453e
Clean up file path for error message
ethanchewy Jun 23, 2020
54ed6ea
Remove todo
ethanchewy Jun 23, 2020
1f6518d
Merge branch 'users/ethanchewy/compositetest2' of https://github.com/…
ethanchewy Jun 23, 2020
9464607
Initial start/framework for output handling
ethanchewy Jun 24, 2020
de37aad
Outline different class vs Handler approach
ethanchewy Jun 24, 2020
26f17d9
Remove InitializeScope
ethanchewy Jun 25, 2020
f73507c
Remove InitializeScope
ethanchewy Jun 25, 2020
25b798b
Merge branch 'users/ethanchewy/compositeOutputsStep' of https://githu…
ethanchewy Jun 25, 2020
57d59fc
Fix Workflow Step Env overiding Parent Env
ethanchewy Jun 25, 2020
0bd97d6
Merge branch 'users/ethanchewy/compositeenv' of https://github.com/ac…
ethanchewy Jun 25, 2020
7ca2caf
Merge branch 'users/ethanchewy/compositeFileTable' of https://github.…
ethanchewy Jun 25, 2020
e38339f
First Approach for Attaching ID + Group ID to each Composite Action Step
ethanchewy Jun 25, 2020
1151e61
Add GroupID to the ActionDefinitionData
ethanchewy Jun 26, 2020
4371a45
starting foundation for handling clean up outputs step
ethanchewy Jun 26, 2020
f1d8655
Pass outputs data to each composite action step to enable set-output …
ethanchewy Jun 29, 2020
a8ba61a
Create ScopeName for whole composite action.
ethanchewy Jun 29, 2020
d87f515
Hook up composite output step to handler => tmmrw implement composite…
ethanchewy Jun 29, 2020
452bec9
Add post composite action step to cleanup outputs => triggers composi…
ethanchewy Jun 30, 2020
9e1f0e9
Fix Outputs Token handling start. Add individual step scope names.
ethanchewy Jun 30, 2020
13911b5
Set up Scope Name and Context Name naming system{
ethanchewy Jun 30, 2020
717e25a
Figured out how to pass Parent Execution Context to clean up step
ethanchewy Jun 30, 2020
d8ba084
Figured out how to pass Parent Execution Context and scope names to
ethanchewy Jun 30, 2020
63b60bd
Merge branch 'users/ethanchewy/compositeOutputsStep' of https://githu…
ethanchewy Jun 30, 2020
84389e1
Add GetOutput function for StepsContext
ethanchewy Jul 1, 2020
196d30a
Generate child scope name correctly if parent scope name is null
ethanchewy Jul 1, 2020
d016c62
Simplify InitializeScope()
ethanchewy Jul 1, 2020
ef42239
Outputs are set correctly and able to get all final outputs in handler
ethanchewy Jul 1, 2020
c92fd34
Parse through Action Outputs
ethanchewy Jul 1, 2020
69a8600
Fix null ScopeName + ContextName in CompositeOutputHandler
ethanchewy Jul 1, 2020
60ff826
Shift over handling of Action Outputs to output handler
ethanchewy Jul 1, 2020
e67cc57
First attempt to fix null retrievals for output variables
ethanchewy Jul 1, 2020
d607f51
Basic Support for Outputs Done.
ethanchewy Jul 6, 2020
43da602
Clean up pt.1
ethanchewy Jul 6, 2020
a277d81
Refactor outputs to avoid using Action Reference
ethanchewy Jul 7, 2020
c1ea7af
Clean up code
ethanchewy Jul 7, 2020
08d873a
Clean up part 2
ethanchewy Jul 7, 2020
9f30200
Add clarifying comments for the output handler
ethanchewy Jul 7, 2020
b94f8e7
Remove TODO
ethanchewy Jul 7, 2020
b63f987
Remove env in composite action scope
ethanchewy Jul 7, 2020
da1e2b0
Clean up
ethanchewy Jul 7, 2020
5d61145
Revert back
ethanchewy Jul 7, 2020
4ccac8c
revert back
ethanchewy Jul 7, 2020
0041023
add back envToken
ethanchewy Jul 7, 2020
4b3ec9f
Remove unnecessary code
ethanchewy Jul 7, 2020
191a096
Merge branch 'users/ethanchewy/compositeenv' of https://github.com/ac…
ethanchewy Jul 7, 2020
20bc6a9
Add file length check
ethanchewy Jul 7, 2020
14a2cbd
Clean up
ethanchewy Jul 7, 2020
d1814f2
Merge branch 'users/ethanchewy/compositeFileTable' of https://github.…
ethanchewy Jul 7, 2020
8d98b17
Fix logging issue
ethanchewy Jul 7, 2020
96bff6a
Figure out how to handle set-env edge cases
ethanchewy Jul 7, 2020
11b9cac
formatting
ethanchewy Jul 7, 2020
b712ba2
Merge branch 'master' into users/ethanchewy/compositeenv
ethanchewy Jul 7, 2020
a58ac2e
fix unit tests
ethanchewy Jul 8, 2020
5178468
Merge branch 'users/ethanchewy/compositeenv' of https://github.com/ac…
ethanchewy Jul 8, 2020
881e8e7
Fix windows unit test syntax error
ethanchewy Jul 8, 2020
b6526db
Merge branch 'users/ethanchewy/compositeenv' of https://github.com/ac…
ethanchewy Jul 8, 2020
3836574
Fix period
ethanchewy Jul 8, 2020
9a1dd80
Sanity check for fileTable add + remove unn. code
ethanchewy Jul 8, 2020
d2d0ecf
revert back
ethanchewy Jul 8, 2020
41a8c8c
Add back line break
ethanchewy Jul 8, 2020
7c57d41
Merge branch 'master' of https://github.com/actions/runner into users…
ethanchewy Jul 8, 2020
35879fc
Fix null errors
ethanchewy Jul 8, 2020
8828263
Address situation if FileTable is null + add sanity check for adding …
ethanchewy Jul 8, 2020
6d7efa9
add line
ethanchewy Jul 8, 2020
9a2b3e2
Revert
ethanchewy Jul 8, 2020
b3eea21
Fix unit tests to instantiate a FileTable
ethanchewy Jul 8, 2020
af9202b
Fix logic for trimming manifestfile path
ethanchewy Jul 8, 2020
635baa5
Add null check
ethanchewy Jul 8, 2020
6122303
Merge branch 'users/ethanchewy/compositeFileTable' of https://github.…
ethanchewy Jul 8, 2020
dd1ee45
Revert
ethanchewy Jul 8, 2020
7d8c3a7
Revert
ethanchewy Jul 8, 2020
71c064d
revert
ethanchewy Jul 8, 2020
5bc2334
spacing
ethanchewy Jul 8, 2020
68b05f6
Add filetable to testing file, remove ? since we know filetable shoul…
ethanchewy Jul 8, 2020
bcba214
Merge branch 'users/ethanchewy/compositeFileTable' of https://github.…
ethanchewy Jul 8, 2020
3536aba
Merge branch 'master' into users/ethanchewy/compositeOutputsStep
ethanchewy Jul 8, 2020
d01e74d
Fix Throw logic
ethanchewy Jul 9, 2020
4a0cb61
Clarify template outputs token
ethanchewy Jul 9, 2020
308c064
Add another type support for outputs to avoid container unit tests er…
ethanchewy Jul 9, 2020
c3c71e9
Add mapping for parity
ethanchewy Jul 9, 2020
55fc11c
Merge branch 'users/ethanchewy/compositeOutputsStep' of https://githu…
ethanchewy Jul 9, 2020
f1a5cb3
Build support for new outputs format
ethanchewy Jul 9, 2020
cea3756
Build support for new outputs format
ethanchewy Jul 9, 2020
a987a08
Merge branch 'users/ethanchewy/compositeOutputsStep' of https://githu…
ethanchewy Jul 9, 2020
90d5cb9
Refactor to avoid duplication of action yaml for workflow yaml
ethanchewy Jul 10, 2020
b7143b2
Merge branch 'users/ethanchewy/compositeOutputsStep' of https://githu…
ethanchewy Jul 10, 2020
3015088
Merge branch 'master' of https://github.com/actions/runner into users…
ethanchewy Jul 13, 2020
a99f32d
revert
ethanchewy Jul 13, 2020
60ddc47
revert
ethanchewy Jul 13, 2020
fcf1e7c
revert
ethanchewy Jul 13, 2020
bb5390a
spacing
ethanchewy Jul 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Runner.Worker/ActionManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,8 @@ public Definition LoadAction(IExecutionContext executionContext, Pipelines.Actio
var compositeAction = definition.Data.Execution as CompositeActionExecutionData;
Trace.Info($"Load {compositeAction.Steps.Count} action steps.");
Trace.Verbose($"Details: {StringUtil.ConvertToJson(compositeAction.Steps)}");
Trace.Info($"Load: {compositeAction.Outputs} outputs");
Trace.Info($"Details: {StringUtil.ConvertToJson(compositeAction.Outputs)}");
}
else
{
Expand Down Expand Up @@ -1222,6 +1224,7 @@ public sealed class CompositeActionExecutionData : ActionExecutionData
public override bool HasPre => false;
public override bool HasPost => false;
public List<Pipelines.ActionStep> Steps { get; set; }
public MappingToken Outputs { get; set; }
}

public abstract class ActionExecutionData
Expand Down
18 changes: 16 additions & 2 deletions src/Runner.Worker/ActionManifestManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ public ActionDefinitionData Load(IExecutionContext executionContext, string mani
}

var actionMapping = token.AssertMapping("action manifest root");
var actionOutputs = default(MappingToken);

foreach (var actionPair in actionMapping)
{
var propertyName = actionPair.Key.AssertString($"action.yml property key");
Expand All @@ -99,6 +101,15 @@ public ActionDefinitionData Load(IExecutionContext executionContext, string mani
actionDefinition.Name = actionPair.Value.AssertString("name").Value;
break;

case "outputs":
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")))
{
actionOutputs = actionPair.Value.AssertMapping("outputs");
break;
}
Trace.Info($"Ignore action property outputs. Outputs for a whole action is not supported yet.");
break;

case "description":
actionDefinition.Description = actionPair.Value.AssertString("description").Value;
break;
Expand All @@ -108,8 +119,9 @@ public ActionDefinitionData Load(IExecutionContext executionContext, string mani
break;

case "runs":
actionDefinition.Execution = ConvertRuns(executionContext, templateContext, actionPair.Value);
actionDefinition.Execution = ConvertRuns(executionContext, templateContext, actionPair.Value, actionOutputs);
break;

default:
Trace.Info($"Ignore action property {propertyName}.");
break;
Expand Down Expand Up @@ -309,7 +321,8 @@ private TemplateContext CreateContext(
private ActionExecutionData ConvertRuns(
IExecutionContext executionContext,
TemplateContext context,
TemplateToken inputsToken)
TemplateToken inputsToken,
MappingToken outputs = null)
{
var runsMapping = inputsToken.AssertMapping("runs");
var usingToken = default(StringToken);
Expand Down Expand Up @@ -439,6 +452,7 @@ private ActionExecutionData ConvertRuns(
return new CompositeActionExecutionData()
{
Steps = stepsLoaded,
Outputs = outputs
};
}
}
Expand Down
77 changes: 51 additions & 26 deletions src/Runner.Worker/ExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ public interface IExecutionContext : IRunnerService
IDictionary<String, IDictionary<String, String>> JobDefaults { get; }
Dictionary<string, VariableValue> JobOutputs { get; }
IDictionary<String, String> EnvironmentVariables { get; }
IDictionary<String, ContextScope> Scopes { get; }
IList<String> FileTable { get; }
StepsContext StepsContext { get; }
DictionaryContextData ExpressionValues { get; }
Expand All @@ -70,10 +69,12 @@ public interface IExecutionContext : IRunnerService

bool EchoOnActionCommand { get; set; }

IExecutionContext FinalizeContext { get; set; }

// Initialize
void InitializeJob(Pipelines.AgentJobRequestMessage message, CancellationToken token);
void CancelToken();
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null);
IExecutionContext CreateChild(Guid recordId, string displayName, string refName, string scopeName, string contextName, Dictionary<string, string> intraActionState = null, int? recordOrder = null, IPagingLogger logger = null);

// logging
bool WriteDebug { get; }
Expand Down Expand Up @@ -105,7 +106,7 @@ public interface IExecutionContext : IRunnerService
// others
void ForceTaskComplete();
void RegisterPostJobStep(IStep step);
void RegisterNestedStep(IStep step, DictionaryContextData inputsData, int location, Dictionary<string, string> envData);
IStep RegisterNestedStep(IActionRunner step, DictionaryContextData inputsData, int location, Dictionary<string, string> envData, Boolean cleanUp = false);
}

public sealed class ExecutionContext : RunnerService, IExecutionContext
Expand Down Expand Up @@ -149,7 +150,6 @@ public sealed class ExecutionContext : RunnerService, IExecutionContext
public IDictionary<String, IDictionary<String, String>> JobDefaults { get; private set; }
public Dictionary<string, VariableValue> JobOutputs { get; private set; }
public IDictionary<String, String> EnvironmentVariables { get; private set; }
public IDictionary<String, ContextScope> Scopes { get; private set; }
public IList<String> FileTable { get; private set; }
public StepsContext StepsContext { get; private set; }
public DictionaryContextData ExpressionValues { get; } = new DictionaryContextData();
Expand All @@ -170,6 +170,8 @@ public sealed class ExecutionContext : RunnerService, IExecutionContext

public bool EchoOnActionCommand { get; set; }

public IExecutionContext FinalizeContext { get; set; }

public TaskResult? Result
{
get
Expand Down Expand Up @@ -270,17 +272,40 @@ public void RegisterPostJobStep(IStep step)
/// 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)
public IStep RegisterNestedStep(
IActionRunner step, DictionaryContextData inputsData, int location,
Dictionary<string, string> envData,
Boolean cleanUp = false)
{
// 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);
// You don't want to create the NewGuid at all. We want to inherit the current node.
// We want to get the ID
// var newGuid = Guid.NewGuid();

// Set Scope Name. Note, for our design, we consider each step in a composite action to have the same scope
// This makes it much simpler to handle their outputs at the end of the Composite Action
var childScopeName = !string.IsNullOrEmpty(this.ScopeName) ? $"{this.ScopeName}.{this.ContextName}" : this.ContextName;

// If the context name is empty and the scope name is empty, we would generate a unique scope name for this child in the following format:
// "__<GUID>"
if (String.IsNullOrEmpty(childScopeName))
{
var newGuid = Guid.NewGuid();
childScopeName = $"__{newGuid}";
}

var childContextName = step.Action.ContextName;

step.ExecutionContext = Root.CreateChild(_record.Id, step.DisplayName, _record.Id.ToString("N"), childScopeName, childContextName, logger: _logger);
step.ExecutionContext.ExpressionValues["inputs"] = inputsData;

// Set Parent Attribute for Clean Up Step
if (cleanUp)
{
step.ExecutionContext.FinalizeContext = this;
}

// 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
Expand All @@ -293,9 +318,11 @@ public void RegisterNestedStep(IStep step, DictionaryContextData inputsData, int
step.ExecutionContext.ExpressionValues["env"] = envContext;

Root.JobSteps.Insert(location, step);

return 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, IPagingLogger logger = null)
{
Trace.Entering();

Expand All @@ -317,7 +344,6 @@ public IExecutionContext CreateChild(Guid recordId, string displayName, string r
}
child.EnvironmentVariables = EnvironmentVariables;
child.JobDefaults = JobDefaults;
child.Scopes = Scopes;
child.FileTable = FileTable;
child.StepsContext = StepsContext;
foreach (var pair in ExpressionValues)
Expand All @@ -344,9 +370,15 @@ public IExecutionContext CreateChild(Guid recordId, string displayName, string r
{
child.InitializeTimelineRecord(_mainTimelineId, recordId, _record.Id, ExecutionContextType.Task, displayName, refName, ++_childTimelineRecordOrder);
}

child._logger = HostContext.CreateService<IPagingLogger>();
child._logger.Setup(_mainTimelineId, recordId);
if (logger != null)
{
child._logger = logger;
}
else
{
child._logger = HostContext.CreateService<IPagingLogger>();
child._logger.Setup(_mainTimelineId, recordId);
}

return child;
}
Expand Down Expand Up @@ -466,7 +498,10 @@ public void SetOutput(string name, string value, out string reference)
{
ArgUtil.NotNullOrEmpty(name, nameof(name));

if (String.IsNullOrEmpty(ContextName))
// Checks if scope name is null or
// if the ScopeName follows the __GUID format which is set as the default value for ScopeNames if null for Composite Actions.
bool scopeNameCondition = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA")) && !String.IsNullOrEmpty(ScopeName) && ScopeName.Length >= 36 && String.Equals(ScopeName.Substring(0, 2), "__") && Guid.TryParse(ScopeName.Substring(2, 36), out Guid test);
if (String.IsNullOrEmpty(ContextName) || scopeNameCondition)
{
reference = null;
return;
Expand Down Expand Up @@ -633,16 +668,6 @@ public void InitializeJob(Pipelines.AgentJobRequestMessage message, Cancellation
// Steps context (StepsRunner manages adding the scoped steps context)
StepsContext = new StepsContext();

// Scopes
Scopes = new Dictionary<String, ContextScope>(StringComparer.OrdinalIgnoreCase);
if (message.Scopes?.Count > 0)
{
foreach (var scope in message.Scopes)
{
Scopes[scope.Name] = scope;
}
}

// File table
FileTable = new List<String>(message.FileTable ?? new string[0]);

Expand Down
31 changes: 29 additions & 2 deletions src/Runner.Worker/Handlers/CompositeActionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ public sealed class CompositeActionHandler : Handler, ICompositeActionHandler
{
public CompositeActionExecutionData Data { get; set; }

private void InitializeScope(IStep step)
{
var stepsContext = step.ExecutionContext.StepsContext;
var scopeName = step.ExecutionContext.ScopeName;
step.ExecutionContext.ExpressionValues["steps"] = stepsContext.GetScope(scopeName);
}

public Task RunAsync(ActionRunStage stage)
{
// Validate args.
Expand All @@ -47,6 +54,12 @@ public Task RunAsync(ActionRunStage stage)

// Add each composite action step to the front of the queue
int location = 0;

Dictionary<string, string> scopesAndContexts = new Dictionary<string, string>();

var parentScopeName = !String.IsNullOrEmpty(ExecutionContext.ScopeName) ? ExecutionContext.ScopeName : ExecutionContext.ContextName;
Trace.Info($"Parent Scope Name {parentScopeName}");

foreach (Pipelines.ActionStep aStep in actionSteps)
{
// Ex:
Expand Down Expand Up @@ -83,12 +96,26 @@ public Task RunAsync(ActionRunStage stage)
actionRunner.Action = aStep;
actionRunner.Stage = stage;
actionRunner.Condition = aStep.Condition;
actionRunner.DisplayName = aStep.DisplayName;

ExecutionContext.RegisterNestedStep(actionRunner, inputsData, location, Environment);
var step = ExecutionContext.RegisterNestedStep(actionRunner, inputsData, location, Environment);

InitializeScope(step);

location++;
}

// Create a step that handles all the composite action steps' outputs
Pipelines.ActionStep cleanOutputsStep = new Pipelines.ActionStep();
cleanOutputsStep.ContextName = ExecutionContext.ContextName;
// Use the same reference type as our composite steps.
cleanOutputsStep.Reference = Action;

var actionRunner2 = HostContext.CreateService<IActionRunner>();
actionRunner2.Action = cleanOutputsStep;
actionRunner2.Stage = ActionRunStage.Main;
actionRunner2.Condition = "always()";
ExecutionContext.RegisterNestedStep(actionRunner2, inputsData, location, Environment, true);

return Task.CompletedTask;
}

Expand Down
70 changes: 70 additions & 0 deletions src/Runner.Worker/Handlers/CompositeActionOutputHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
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(CompositeActionOutputHandler))]
public interface ICompositeActionOutputHandler : IHandler
{
CompositeActionExecutionData Data { get; set; }
}

public sealed class CompositeActionOutputHandler : Handler, ICompositeActionOutputHandler
{
public CompositeActionExecutionData Data { get; set; }
public Task RunAsync(ActionRunStage stage)
{
// Evaluate the mapped outputs value
if (Data.Outputs != null)
{
// Evaluate the outputs in the steps context to easily retrieve the values
var evaluator = ExecutionContext.ToPipelineTemplateEvaluator();
DictionaryContextData actionOutputs = evaluator.EvaluateCompositeOutputs(Data.Outputs, ExecutionContext.ExpressionValues, ExecutionContext.ExpressionFunctions);

// Each pair is structured like this
// We ignore "description" for now
// "key": "output_id",
// {
// "value": {
// "t": 2,
// "d": [
// {
// "k": "description",
// "v": "string (can be null)"
// },
// {
// "k": "value",
// "v": "string/expression"
// }
// ]
// }
// }
foreach (var pair in actionOutputs)
{
var outputsName = pair.Key;
var outputsAttributes = pair.Value as DictionaryContextData;
outputsAttributes.TryGetValue("value", out var val);
var outputsValue = val as StringContextData;

// Set output in the whole composite scope.
if (!String.IsNullOrEmpty(outputsName) && !String.IsNullOrEmpty(outputsValue))
{
ExecutionContext.FinalizeContext.SetOutput(outputsName, outputsValue, out string test);
}
}
}

return Task.CompletedTask;
}
}
}
12 changes: 10 additions & 2 deletions src/Runner.Worker/Handlers/HandlerFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,16 @@ public IHandler Create(
}
else if (data.ExecutionType == ActionExecutionType.Composite)
{
handler = HostContext.CreateService<ICompositeActionHandler>();
(handler as ICompositeActionHandler).Data = data as CompositeActionExecutionData;
if (executionContext.FinalizeContext == null)
{
handler = HostContext.CreateService<ICompositeActionHandler>();
(handler as ICompositeActionHandler).Data = data as CompositeActionExecutionData;
}
else
{
handler = HostContext.CreateService<ICompositeActionOutputHandler>();
(handler as ICompositeActionOutputHandler).Data = data as CompositeActionExecutionData;
}
}
else
{
Expand Down
Loading