diff --git a/src/Runner.Worker/ActionRunner.cs b/src/Runner.Worker/ActionRunner.cs index 8331e9915..b0c245ac3 100644 --- a/src/Runner.Worker/ActionRunner.cs +++ b/src/Runner.Worker/ActionRunner.cs @@ -143,8 +143,10 @@ namespace GitHub.Runner.Worker var templateEvaluator = ExecutionContext.ToPipelineTemplateEvaluator(); var inputs = templateEvaluator.EvaluateStepInputs(Action.Inputs, ExecutionContext.ExpressionValues, ExecutionContext.ExpressionFunctions); + var userInputs = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair input in inputs) { + userInputs.Add(input.Key); string message = ""; if (definition.Data?.Deprecated?.TryGetValue(input.Key, out message) == true) { @@ -152,13 +154,15 @@ namespace GitHub.Runner.Worker } } + var validInputs = new HashSet(StringComparer.OrdinalIgnoreCase); // Merge the default inputs from the definition if (definition.Data?.Inputs != null) { var manifestManager = HostContext.GetService(); - foreach (var input in (definition.Data?.Inputs)) + foreach (var input in definition.Data.Inputs) { string key = input.Key.AssertString("action input name").Value; + validInputs.Add(key); if (!inputs.ContainsKey(key)) { inputs[key] = manifestManager.EvaluateDefaultInput(ExecutionContext, key, input.Value); @@ -166,6 +170,14 @@ namespace GitHub.Runner.Worker } } + foreach (var input in userInputs) + { + if (!validInputs.Contains(input)) + { + ExecutionContext.Warning($"Unexpected input '{input}', valid inputs are ['{string.Join("', '", validInputs)}']"); + } + } + // Load the action environment. ExecutionContext.Debug("Loading env"); var environment = new Dictionary(VarUtil.EnvironmentVariableKeyComparer); diff --git a/src/Test/L0/Worker/ActionRunnerL0.cs b/src/Test/L0/Worker/ActionRunnerL0.cs index 24ff73f4f..73f215a3f 100644 --- a/src/Test/L0/Worker/ActionRunnerL0.cs +++ b/src/Test/L0/Worker/ActionRunnerL0.cs @@ -278,6 +278,59 @@ namespace GitHub.Runner.Common.Tests.Worker Assert.Equal("${{ matrix.node }}", _actionRunner.DisplayName); } + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Worker")] + public async void WarnInvalidInputs() + { + //Arrange + Setup(); + var actionId = Guid.NewGuid(); + var actionInputs = new MappingToken(null, null, null); + actionInputs.Add(new StringToken(null, null, null, "input1"), new StringToken(null, null, null, "test1")); + actionInputs.Add(new StringToken(null, null, null, "input2"), new StringToken(null, null, null, "test2")); + actionInputs.Add(new StringToken(null, null, null, "invalid1"), new StringToken(null, null, null, "invalid1")); + actionInputs.Add(new StringToken(null, null, null, "invalid2"), new StringToken(null, null, null, "invalid2")); + var action = new Pipelines.ActionStep() + { + Name = "action", + Id = actionId, + Reference = new Pipelines.ContainerRegistryReference() + { + Image = "ubuntu:16.04" + }, + Inputs = actionInputs + }; + + _actionRunner.Action = action; + + Dictionary finialInputs = new Dictionary(); + _handlerFactory.Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny())) + .Callback((IExecutionContext executionContext, Pipelines.ActionStepDefinitionReference actionReference, IStepHost stepHost, ActionExecutionData data, Dictionary inputs, Dictionary environment, Variables runtimeVariables, string taskDirectory) => + { + finialInputs = inputs; + }) + .Returns(new Mock().Object); + + //Act + await _actionRunner.RunAsync(); + + foreach (var input in finialInputs) + { + _hc.GetTrace().Info($"Input: {input.Key}={input.Value}"); + } + + //Assert + Assert.Equal("test1", finialInputs["input1"]); + Assert.Equal("test2", finialInputs["input2"]); + Assert.Equal("github", finialInputs["input3"]); + Assert.Equal("invalid1", finialInputs["invalid1"]); + Assert.Equal("invalid2", finialInputs["invalid2"]); + + _ec.Verify(x => x.AddIssue(It.Is(s => s.Message.Contains("Unexpected input 'invalid1'")), It.IsAny()), Times.Once); + _ec.Verify(x => x.AddIssue(It.Is(s => s.Message.Contains("Unexpected input 'invalid2'")), It.IsAny()), Times.Once); + } + private void Setup([CallerMemberName] string name = "") { _ecTokenSource?.Dispose();