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 Support for Multiple Run Steps #549

Merged
merged 13 commits into from
Jun 23, 2020

Conversation

ethanchewy
Copy link
Contributor

@ethanchewy ethanchewy commented Jun 15, 2020

In this PR, we support executing multiple run steps with custom inputs in a composite action. You can use this feature by writing using: "composite" in the action.yml file. Our goal for our first version is to be able to run simple, multiple steps in an action.yml file. This first step allows for users to run a list of steps in the action.yml file rather than invoking a javascript entry point. This will eventually allow us to support nesting of actions within an action, etc.

Here is an in-depth example:

ethanchewy/test-composite/action.yml:

name: "Composite Action Test v2"
description: "Run a simple composite action"
inputs:
  your_name:
    description: 'lol'
    default: 'Ethan'
runs:
    using: "composite"
    steps: 
        - run: echo hello ${{inputs.your_name}}
        - run: echo sha ${{github.sha}}
        - run: echo os ${{runner.os}}
        - run: |
            echo job index ${{ strategy.job-index }}

workflow.yml

name: Composite Action
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]
jobs:
  build:
    runs-on: self-hosted
    - uses: actions/checkout@v2
    - uses: ethanchewy/test-composite@v5
      with:
        your_name: "Octocat"
    - name: step 1
      run: echo Workflow step 1
    - name: step 2
      run: echo Workflow step 2

Github UI Action View: https://github.com/ethanchewy/testing-actions/runs/778225962?check_suite_focus=true

What this doesn't support yet (for future PRs):

  • Input, Output, Env flow (ex: All nested steps should have access to its parents' input variables and nested steps can overwrite the input variables)
  • Nesting other actions in composite actions (supporting in a step the terminology "using: __")
  • etc.

How this was implemented at a high level:
We have one queue that keeps track of the composite actions transformed into a job step (in the process, it’s added to the Root node) => then we requeue in a way where these composite actions are executed first.
None of the steps are actually evaluated by the CompositeActionsHandler but they are pushed to the front of the job step queue and handled by other handlers depending on its type (ex: Script handler).

This is an experimental feature that can only be turned on via turning the feature flag on:
export TESTING_COMPOSITE_ACTIONS_ALPHA=1
./run.sh

@ethanchewy ethanchewy force-pushed the users/ethanchewy/compositetest2 branch 2 times, most recently from c4848ee to c6c0571 Compare June 17, 2020 21:42
@ethanchewy ethanchewy changed the title Composite Actions Support for Multiple Run Steps (In Progress) Composite Actions Support for Multiple Run Steps Jun 17, 2020
@ethanchewy ethanchewy force-pushed the users/ethanchewy/compositetest2 branch from 76af6f9 to 038e5e2 Compare June 18, 2020 13:48
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($"Action steps: {compositeAction.Steps}.");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this will print Action steps: Object

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the json representation is desired (too noisy? esp with large inline scripts?) then see StringUtil or IOUtil (has function to convert to/from json)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@TingluoHuang thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, compositeAction.Steps is of type List<Pipelines.ActionStep>. If we were to output this, what would be the best way to output the values in a json string format?

Right now, this Trace.info statement returns the object string representation: GitHub.DistributedTask.Pipelines.ActionStep

If we were to print out the steps, it might make more sense to print them out in the LoadCompositeSteps function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From @TingluoHuang (thanks Ting!!) :

Verbose will shows up only in dev mode (you build the runner from source code.)
Trace.Info($"Load {compositeAction.Steps.Count} action steps.");
Trace.Verbose($"Details: {StringUtil.ConvertToJson(compositeAction.Steps}");

So, I'll do that.

{
if (stepsLoaded == null)
{
throw new ArgumentNullException($"No steps provided.");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this error give the user enough context to know where the problem is? Like does it convey that an error happened when loading the path-to-action.yml

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess we would have the same problem with the other place too (in this file)

Copy link
Contributor Author

@ethanchewy ethanchewy Jun 19, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this error makes sense and is appropriate.

At the top of ConvertRuns:
var stepsLoaded = default(List<Pipelines.ActionStep>);

Then, we check in the action.yml file to see if there is any steps token:

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!");

If there are any issues with loading the steps, there would be an error reported in the LoadCompositeSteps() or LoadStep() function in the pipeline template evaulator. If the steps attribute is empty, there would be no error and LoadCompositeSteps would return the default value of List which is equivalent to null.

Thus, if stepsLoaded is null, it must be because there was no steps attribute written in the action.yml by the user.

Thus, I believe throwing this message is appropriate.

Copy link
Contributor Author

@ethanchewy ethanchewy Jun 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Add a more helpful error message + including file name, etc. to show user that it's because of their yaml file

(Just add TODO for now)

// placed after the queued composite steps
// This will take only O(n+m) time which would be just as efficient if we refactored the code of JobSteps to a PriorityQueue
// This temp Queue is created in the CompositeActionHandler.
var newGuid = Guid.NewGuid();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm worried a little bit because we're not adding the contexts from the job message, like we normally do when creating steps.

I know this is what Post does, but wouldnt surprise me if there is a bug there too.

It may be safe, since we're copying the root node (maybe job message contexts copied there first so safe?)

Let's add a TODO comment in the code like "// TODO: confirm whether not copying message contexts is safe".

I can help look into this more. It's tedious because values are delay expanded sometimes (sometimes evaluated early, like DisplayName). I wish the code were designed simpler... hmm thinking about alternatives.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added TODO. Leaving open for visibility.

Copy link
Contributor Author

@ethanchewy ethanchewy Jun 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After more testing for the env flow, I realized that the display name does not evaluate the env variables. This has to do with not passing the context. See: #557 (comment)
Ex: https://github.com/ethanchewy/testing-actions/runs/795652488?check_suite_focus=true
Building in support will occur in the env flow PR: #557

Never mind, in both the composite action and workflow steps, the environment contexts are evaluated correctly: https://github.com/ethanchewy/testing-actions/actions/runs/143774560

Still investigating but for now I think the contexts are handled correctly for the composite action steps.

Copy link
Contributor Author

@ethanchewy ethanchewy Jun 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tl;dr The context is exposed correctly in env flow PR and solves this concern

In the env flow PR, I handle the contexts for each correctly: https://github.com/actions/runner/pull/557/files#diff-e3d54b1d0cbdce4d358272df29857cf2R225 by setting the step.EnvironmentVariables to the environment variables.

Demo: https://github.com/ethanchewy/testing-actions/actions/runs/143774560

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, since we add each of our composite steps to JobSteps, eventually each of the steps will be run via the ScriptHandler.

In the ScriptHandler, it exposes the context to the step/job: https://github.com/actions/runner/blob/master/src/Runner.Worker/Handlers/ScriptHandler.cs#L248-L258

@ethanchewy

This comment has been minimized.

@@ -0,0 +1,97 @@
using System.IO;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sort

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

system first

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

named at the bottom

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(0, step);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Replace 0 with location so that the steps are ordered.

"job",
"runner",
"env",
"hashFiles(1,255)"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        "always(0,0)",
        "failure(0,0)",
        "cancelled(0,0)",
        "success(0,0)",
        "hashFiles(1,255)"

@@ -400,7 +426,8 @@ public class PipelineTemplateEvaluator
private TemplateContext CreateContext(
DictionaryContextData contextData,
IList<IFunctionInfo> expressionFunctions,
IEnumerable<KeyValuePair<String, Object>> expressionState = null)
IEnumerable<KeyValuePair<String, Object>> expressionState = null,
bool setMissingContext = true)
Copy link
Contributor Author

@ethanchewy ethanchewy Jun 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe switch to false when going to server (and also switch the logic in the function to reflect this). We'll put these changes in the server side.

@ethanchewy ethanchewy merged commit a0942ed into master Jun 23, 2020
AdamOlech pushed a commit to antmicro/runner that referenced this pull request Jan 28, 2021
* Composite Action Run Steps

* Clean up trace messages + add Trace debug in ActionManager

* Change String to string

* Add comma to Composite

* Change JobSteps to a List, Change Register Step function name

* Add TODO, remove unn. content

* Remove unnecessary code

* Fix unit tests

* Add verbose trace logs which are only viewable by devs

* Sort usings in Composite Action Handler

* Change 0 to location

* Update context variables in composite action yaml

* Add helpful error message for null steps
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants