mirror of
https://github.com/actions/runner.git
synced 2025-12-11 21:06:55 +00:00
include step.env as part of env context. (#300)
This commit is contained in:
@@ -3,8 +3,6 @@
|
|||||||
<packageSources>
|
<packageSources>
|
||||||
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
||||||
<clear />
|
<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" />
|
<add key="api.nuget.org" value="https://api.nuget.org/v3/index.json" />
|
||||||
</packageSources>
|
</packageSources>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
@@ -179,17 +179,15 @@ namespace GitHub.Runner.Worker
|
|||||||
ExecutionContext.Debug("Loading env");
|
ExecutionContext.Debug("Loading env");
|
||||||
var environment = new Dictionary<String, String>(VarUtil.EnvironmentVariableKeyComparer);
|
var environment = new Dictionary<String, String>(VarUtil.EnvironmentVariableKeyComparer);
|
||||||
|
|
||||||
// Apply environment set using ##[set-env] first since these are job level env
|
#if OS_WINDOWS
|
||||||
foreach (var env in ExecutionContext.EnvironmentVariables)
|
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;
|
environment[env.Key] = env.Value.ToString();
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply action's intra-action state at last
|
// Apply action's intra-action state at last
|
||||||
|
|||||||
@@ -76,10 +76,33 @@ namespace GitHub.Runner.Worker
|
|||||||
// Start
|
// Start
|
||||||
step.ExecutionContext.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)
|
if (step is IActionRunner actionStep)
|
||||||
{
|
{
|
||||||
|
// Set GITHUB_ACTION
|
||||||
step.ExecutionContext.SetGitHubContext("action", actionStep.Action.Name);
|
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
|
// Initialize scope
|
||||||
|
|||||||
@@ -35,6 +35,19 @@ namespace GitHub.DistributedTask.Pipelines.ContextData
|
|||||||
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(DictionaryContextData)}' was expected.");
|
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)]
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||||
public static StringContextData AssertString(
|
public static StringContextData AssertString(
|
||||||
this PipelineContextData value,
|
this PipelineContextData value,
|
||||||
|
|||||||
@@ -314,6 +314,12 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
githubContext.Add("event", JToken.Parse("{\"foo\":\"bar\"}").ToPipelineContextData());
|
githubContext.Add("event", JToken.Parse("{\"foo\":\"bar\"}").ToPipelineContextData());
|
||||||
_context.Add("github", githubContext);
|
_context.Add("github", githubContext);
|
||||||
|
|
||||||
|
#if OS_WINDOWS
|
||||||
|
_context["env"] = new DictionaryContextData();
|
||||||
|
#else
|
||||||
|
_context["env"] = new CaseSensitiveDictionaryContextData();
|
||||||
|
#endif
|
||||||
|
|
||||||
_ec = new Mock<IExecutionContext>();
|
_ec = new Mock<IExecutionContext>();
|
||||||
_ec.Setup(x => x.ExpressionValues).Returns(_context);
|
_ec.Setup(x => x.ExpressionValues).Returns(_context);
|
||||||
_ec.Setup(x => x.IntraActionState).Returns(new Dictionary<string, string>());
|
_ec.Setup(x => x.IntraActionState).Returns(new Dictionary<string, string>());
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
private Mock<IExecutionContext> _ec;
|
private Mock<IExecutionContext> _ec;
|
||||||
private StepsRunner _stepsRunner;
|
private StepsRunner _stepsRunner;
|
||||||
private Variables _variables;
|
private Variables _variables;
|
||||||
|
private Dictionary<string, string> _env;
|
||||||
private DictionaryContextData _contexts;
|
private DictionaryContextData _contexts;
|
||||||
private JobContext _jobContext;
|
private JobContext _jobContext;
|
||||||
private StepsContext _stepContext;
|
private StepsContext _stepContext;
|
||||||
@@ -32,6 +33,11 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
_variables = new Variables(
|
_variables = new Variables(
|
||||||
hostContext: hc,
|
hostContext: hc,
|
||||||
copy: variablesToCopy);
|
copy: variablesToCopy);
|
||||||
|
_env = new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{"env1", "1"},
|
||||||
|
{"test", "github_actions"}
|
||||||
|
};
|
||||||
_ec = new Mock<IExecutionContext>();
|
_ec = new Mock<IExecutionContext>();
|
||||||
_ec.SetupAllProperties();
|
_ec.SetupAllProperties();
|
||||||
_ec.Setup(x => x.Variables).Returns(_variables);
|
_ec.Setup(x => x.Variables).Returns(_variables);
|
||||||
@@ -399,18 +405,98 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
// Setup the step.
|
||||||
var step = new Mock<IStep>();
|
var step = new Mock<IActionRunner>();
|
||||||
step.Setup(x => x.Condition).Returns(condition);
|
step.Setup(x => x.Condition).Returns(condition);
|
||||||
step.Setup(x => x.ContinueOnError).Returns(new BooleanToken(null, null, null, continueOnError));
|
step.Setup(x => x.ContinueOnError).Returns(new BooleanToken(null, null, null, continueOnError));
|
||||||
step.Setup(x => x.RunAsync()).Returns(Task.CompletedTask);
|
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.
|
// Setup the step execution context.
|
||||||
var stepContext = new Mock<IExecutionContext>();
|
var stepContext = new Mock<IExecutionContext>();
|
||||||
stepContext.SetupAllProperties();
|
stepContext.SetupAllProperties();
|
||||||
stepContext.Setup(x => x.Variables).Returns(_variables);
|
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(_contexts);
|
||||||
stepContext.Setup(x => x.JobContext).Returns(_jobContext);
|
stepContext.Setup(x => x.JobContext).Returns(_jobContext);
|
||||||
stepContext.Setup(x => x.StepsContext).Returns(_stepContext);
|
stepContext.Setup(x => x.StepsContext).Returns(_stepContext);
|
||||||
@@ -428,7 +514,7 @@ namespace GitHub.Runner.Common.Tests.Worker
|
|||||||
return step;
|
return step;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string FormatSteps(IEnumerable<Mock<IStep>> steps)
|
private string FormatSteps(IEnumerable<Mock<IActionRunner>> steps)
|
||||||
{
|
{
|
||||||
return String.Join(
|
return String.Join(
|
||||||
" ; ",
|
" ; ",
|
||||||
|
|||||||
Reference in New Issue
Block a user