Skip to content

Commit

Permalink
include step.env as part of env context. (actions#300)
Browse files Browse the repository at this point in the history
  • Loading branch information
TingluoHuang authored Jan 27, 2020
1 parent eb78d19 commit 0a6bac3
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 16 deletions.
2 changes: 0 additions & 2 deletions src/NuGet.Config
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
<packageSources>
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
<clear />
<add key="dotnet-core" value="https://www.myget.org/F/dotnet-core/api/v3/index.json" />
<add key="dotnet-buildtools" value="https://www.myget.org/F/dotnet-buildtools/api/v3/index.json" />
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
</configuration>
18 changes: 8 additions & 10 deletions src/Runner.Worker/ActionRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,17 +179,15 @@ public async Task RunAsync()
ExecutionContext.Debug("Loading env");
var environment = new Dictionary<String, String>(VarUtil.EnvironmentVariableKeyComparer);

// Apply environment set using ##[set-env] first since these are job level env
foreach (var env in ExecutionContext.EnvironmentVariables)
#if OS_WINDOWS
var envContext = ExecutionContext.ExpressionValues["env"] as DictionaryContextData;
#else
var envContext = ExecutionContext.ExpressionValues["env"] as CaseSensitiveDictionaryContextData;
#endif
// Apply environment from env context, env context contains job level env and action's evn block
foreach (var env in envContext)
{
environment[env.Key] = env.Value ?? string.Empty;
}

// Apply action's env block later.
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(Action.Environment, ExecutionContext.ExpressionValues, VarUtil.EnvironmentVariableKeyComparer);
foreach (var env in actionEnvironment)
{
environment[env.Key] = env.Value ?? string.Empty;
environment[env.Key] = env.Value.ToString();
}

// Apply action's intra-action state at last
Expand Down
25 changes: 24 additions & 1 deletion src/Runner.Worker/StepsRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,33 @@ public async Task RunAsync(IExecutionContext jobContext)
// Start
step.ExecutionContext.Start();

// Set GITHUB_ACTION
// Populate env context for each step
Trace.Info("Initialize Env context for step");
#if OS_WINDOWS
var envContext = new DictionaryContextData();
#else
var envContext = new CaseSensitiveDictionaryContextData();
#endif
step.ExecutionContext.ExpressionValues["env"] = envContext;
foreach (var pair in step.ExecutionContext.EnvironmentVariables)
{
envContext[pair.Key] = new StringContextData(pair.Value ?? string.Empty);
}

if (step is IActionRunner actionStep)
{
// Set GITHUB_ACTION
step.ExecutionContext.SetGitHubContext("action", actionStep.Action.Name);

// Evaluate and merge action's env block to env context
var templateTrace = step.ExecutionContext.ToTemplateTraceWriter();
var schema = new PipelineTemplateSchemaFactory().CreateSchema();
var templateEvaluator = new PipelineTemplateEvaluator(templateTrace, schema);
var actionEnvironment = templateEvaluator.EvaluateStepEnvironment(actionStep.Action.Environment, step.ExecutionContext.ExpressionValues, VarUtil.EnvironmentVariableKeyComparer);
foreach (var env in actionEnvironment)
{
envContext[env.Key] = new StringContextData(env.Value ?? string.Empty);
}
}

// Initialize scope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ public static DictionaryContextData AssertDictionary(
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(DictionaryContextData)}' was expected.");
}

[EditorBrowsable(EditorBrowsableState.Never)]
public static CaseSensitiveDictionaryContextData AssertCaseSensitiveDictionary(
this PipelineContextData value,
String objectDescription)
{
if (value is CaseSensitiveDictionaryContextData dictionary)
{
return dictionary;
}

throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(CaseSensitiveDictionaryContextData)}' was expected.");
}

[EditorBrowsable(EditorBrowsableState.Never)]
public static StringContextData AssertString(
this PipelineContextData value,
Expand Down
6 changes: 6 additions & 0 deletions src/Test/L0/Worker/ActionRunnerL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,12 @@ private void Setup([CallerMemberName] string name = "")
githubContext.Add("event", JToken.Parse("{\"foo\":\"bar\"}").ToPipelineContextData());
_context.Add("github", githubContext);

#if OS_WINDOWS
_context["env"] = new DictionaryContextData();
#else
_context["env"] = new CaseSensitiveDictionaryContextData();
#endif

_ec = new Mock<IExecutionContext>();
_ec.Setup(x => x.ExpressionValues).Returns(_context);
_ec.Setup(x => x.IntraActionState).Returns(new Dictionary<string, string>());
Expand Down
92 changes: 89 additions & 3 deletions src/Test/L0/Worker/StepsRunnerL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public sealed class StepsRunnerL0
private Mock<IExecutionContext> _ec;
private StepsRunner _stepsRunner;
private Variables _variables;
private Dictionary<string, string> _env;
private DictionaryContextData _contexts;
private JobContext _jobContext;
private StepsContext _stepContext;
Expand All @@ -32,6 +33,11 @@ private TestHostContext CreateTestContext([CallerMemberName] String testName = "
_variables = new Variables(
hostContext: hc,
copy: variablesToCopy);
_env = new Dictionary<string, string>()
{
{"env1", "1"},
{"test", "github_actions"}
};
_ec = new Mock<IExecutionContext>();
_ec.SetupAllProperties();
_ec.Setup(x => x.Variables).Returns(_variables);
Expand Down Expand Up @@ -399,18 +405,98 @@ public async Task TreatsConditionErrorAsFailure()
}
}

private Mock<IStep> CreateStep(TaskResult result, string condition, Boolean continueOnError = false)
[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task StepEnvOverrideJobEnvContext()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var env1 = new MappingToken(null, null, null);
env1.Add(new StringToken(null, null, null, "env1"), new StringToken(null, null, null, "100"));
env1.Add(new StringToken(null, null, null, "env2"), new BasicExpressionToken(null, null, null, "env.test"));
var step1 = CreateStep(TaskResult.Succeeded, "success()", env: env1);

_ec.Object.Result = null;

_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object }));

// Act.
await _stepsRunner.RunAsync(jobContext: _ec.Object);

// Assert.
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"));
#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"));
#endif
}
}

[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public async Task PopulateEnvContextForEachStep()
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var env1 = new MappingToken(null, null, null);
env1.Add(new StringToken(null, null, null, "env1"), new StringToken(null, null, null, "100"));
env1.Add(new StringToken(null, null, null, "env2"), new BasicExpressionToken(null, null, null, "env.test"));
var step1 = CreateStep(TaskResult.Succeeded, "success()", env: env1);

var env2 = new MappingToken(null, null, null);
env2.Add(new StringToken(null, null, null, "env1"), new StringToken(null, null, null, "1000"));
env2.Add(new StringToken(null, null, null, "env3"), new BasicExpressionToken(null, null, null, "env.test"));
var step2 = CreateStep(TaskResult.Succeeded, "success()", env: env2);

_ec.Object.Result = null;

_ec.Setup(x => x.JobSteps).Returns(new Queue<IStep>(new[] { step1.Object, step2.Object }));

// Act.
await _stepsRunner.RunAsync(jobContext: _ec.Object);

// 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"));
#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"));
#endif
}
}

private Mock<IActionRunner> CreateStep(TaskResult result, string condition, Boolean continueOnError = false, MappingToken env = null)
{
// Setup the step.
var step = new Mock<IStep>();
var step = new Mock<IActionRunner>();
step.Setup(x => x.Condition).Returns(condition);
step.Setup(x => x.ContinueOnError).Returns(new BooleanToken(null, null, null, continueOnError));
step.Setup(x => x.RunAsync()).Returns(Task.CompletedTask);
step.Setup(x => x.Action)
.Returns(new DistributedTask.Pipelines.ActionStep()
{
Name = "Test",
Id = Guid.NewGuid(),
Environment = env
});

// Setup the step execution context.
var stepContext = new Mock<IExecutionContext>();
stepContext.SetupAllProperties();
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.JobContext).Returns(_jobContext);
stepContext.Setup(x => x.StepsContext).Returns(_stepContext);
Expand All @@ -428,7 +514,7 @@ private Mock<IStep> CreateStep(TaskResult result, string condition, Boolean cont
return step;
}

private string FormatSteps(IEnumerable<Mock<IStep>> steps)
private string FormatSteps(IEnumerable<Mock<IActionRunner>> steps)
{
return String.Join(
" ; ",
Expand Down

0 comments on commit 0a6bac3

Please sign in to comment.