public void Evaluate_ContainerAction_Env() { try { //Arrange Setup(); var actionManifest = new ActionManifestManager(); actionManifest.Initialize(_hc); var environment = new MappingToken(null, null, null); environment.Add(new StringToken(null, null, null, "hello"), new BasicExpressionToken(null, null, null, "inputs.greeting")); environment.Add(new StringToken(null, null, null, "test"), new StringToken(null, null, null, "test")); var inputsContext = new DictionaryContextData(); inputsContext.Add("greeting", new StringContextData("hello")); var evaluateContext = new Dictionary <string, PipelineContextData>(StringComparer.OrdinalIgnoreCase); evaluateContext["inputs"] = inputsContext; //Act var result = actionManifest.EvaluateContainerEnvironment(_ec.Object, environment, evaluateContext); //Assert Assert.Equal("hello", result["hello"]); Assert.Equal("test", result["test"]); Assert.Equal(2, result.Count); } finally { Teardown(); } }
public async Task PopulateEnvContextAfterSetupStepsContext() { 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")); var step1 = CreateStep(hc, TaskResult.Succeeded, "success()", env: env1, name: "foo", setOutput: true); 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, "env2"), new BasicExpressionToken(null, null, null, "steps.foo.outputs.test")); var step2 = CreateStep(hc, 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("something", _ec.Object.ExpressionValues["env"].AssertDictionary("env")["env2"].AssertString("something")); #else Assert.Equal("1000", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env1"].AssertString("1000")); Assert.Equal("something", _ec.Object.ExpressionValues["env"].AssertCaseSensitiveDictionary("env")["env2"].AssertString("something")); #endif } }
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(hc, 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(hc, 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 } }
public async void SetGitHubContextActionRepoRef() { //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")); var action = new Pipelines.ActionStep() { Name = "action", Id = actionId, Reference = new Pipelines.RepositoryPathReference() { Name = "actions/test", Ref = "master" }, Inputs = actionInputs }; _actionRunner.Action = action; Dictionary <string, string> finialInputs = new Dictionary <string, string>(); _handlerFactory.Setup(x => x.Create(It.IsAny <IExecutionContext>(), It.IsAny <ActionStepDefinitionReference>(), It.IsAny <IStepHost>(), It.IsAny <ActionExecutionData>(), It.IsAny <Dictionary <string, string> >(), It.IsAny <Dictionary <string, string> >(), It.IsAny <Variables>(), It.IsAny <string>(), It.IsAny <List <JobExtensionRunner> >())) .Callback((IExecutionContext executionContext, Pipelines.ActionStepDefinitionReference actionReference, IStepHost stepHost, ActionExecutionData data, Dictionary <string, string> inputs, Dictionary <string, string> environment, Variables runtimeVariables, string taskDirectory, List <JobExtensionRunner> localActionContainerSetupSteps) => { finialInputs = inputs; }) .Returns(new Mock <IHandler>().Object); //Act await _actionRunner.RunAsync(); //Assert _ec.Verify(x => x.SetGitHubContext("action_repository", "actions/test"), Times.Once); _ec.Verify(x => x.SetGitHubContext("action_ref", "master"), Times.Once); action = new Pipelines.ActionStep() { Name = "action", Id = actionId, Reference = new Pipelines.ScriptReference(), Inputs = actionInputs }; _actionRunner.Action = action; _hc.EnqueueInstance <IDefaultStepHost>(_defaultStepHost.Object); _hc.EnqueueInstance(_fileCommandManager.Object); //Act await _actionRunner.RunAsync(); //Assert _ec.Verify(x => x.SetGitHubContext("action_repository", null), Times.Once); _ec.Verify(x => x.SetGitHubContext("action_ref", null), Times.Once); }
private void HandleMappingWithAllLooseProperties( DefinitionInfo mappingDefinition, DefinitionInfo keyDefinition, DefinitionInfo valueDefinition, MappingToken mapping) { TemplateToken nextValue; var keys = new HashSet <String>(StringComparer.OrdinalIgnoreCase); while (m_objectReader.AllowLiteral(out LiteralToken rawLiteral)) { var nextKeyScalar = ParseScalar(rawLiteral, mappingDefinition.AllowedContext); // Expression if (nextKeyScalar is ExpressionToken) { // Legal if (mappingDefinition.AllowedContext.Length > 0) { m_memory.AddBytes(nextKeyScalar); nextValue = ReadValue(valueDefinition); mapping.Add(nextKeyScalar, nextValue); } // Illegal else { m_context.Error(nextKeyScalar, TemplateStrings.ExpressionNotAllowed()); SkipValue(); } continue; } // Not a string, convert if (!(nextKeyScalar is StringToken nextKey)) { nextKey = new StringToken(nextKeyScalar.FileId, nextKeyScalar.Line, nextKeyScalar.Column, nextKeyScalar.ToString()); } // Duplicate if (!keys.Add(nextKey.Value)) { m_context.Error(nextKey, TemplateStrings.ValueAlreadyDefined(nextKey.Value)); SkipValue(); continue; } // Validate Validate(nextKey, keyDefinition); m_memory.AddBytes(nextKey); // Add the pair nextValue = ReadValue(valueDefinition); mapping.Add(nextKey, nextValue); } ExpectMappingEnd(); }
// todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere internal static TemplateToken ConvertToTemplateToken(ContainerResource resource) { var result = new MappingToken(null, null, null); var image = resource.Image; if (!string.IsNullOrEmpty(image)) { result.Add(new StringToken(null, null, null, "image"), new StringToken(null, null, null, image)); } var options = resource.Options; if (!string.IsNullOrEmpty(options)) { result.Add(new StringToken(null, null, null, "options"), new StringToken(null, null, null, options)); } var environment = resource.Environment; if (environment?.Count > 0) { var mapping = new MappingToken(null, null, null); foreach (var pair in environment) { mapping.Add(new StringToken(null, null, null, pair.Key), new StringToken(null, null, null, pair.Value)); } result.Add(new StringToken(null, null, null, "env"), mapping); } var ports = resource.Ports; if (ports?.Count > 0) { var sequence = new SequenceToken(null, null, null); foreach (var item in ports) { sequence.Add(new StringToken(null, null, null, item)); } result.Add(new StringToken(null, null, null, "ports"), sequence); } var volumes = resource.Volumes; if (volumes?.Count > 0) { var sequence = new SequenceToken(null, null, null); foreach (var item in volumes) { sequence.Add(new StringToken(null, null, null, item)); } result.Add(new StringToken(null, null, null, "volumes"), sequence); } return(result); }
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.RepositoryPathReference() { Name = "actions/runner", Ref = "v1" }, Inputs = actionInputs }; _actionRunner.Action = action; Dictionary <string, string> finialInputs = new Dictionary <string, string>(); _handlerFactory.Setup(x => x.Create(It.IsAny <IExecutionContext>(), It.IsAny <ActionStepDefinitionReference>(), It.IsAny <IStepHost>(), It.IsAny <ActionExecutionData>(), It.IsAny <Dictionary <string, string> >(), It.IsAny <Dictionary <string, string> >(), It.IsAny <Variables>(), It.IsAny <string>())) .Callback((IExecutionContext executionContext, Pipelines.ActionStepDefinitionReference actionReference, IStepHost stepHost, ActionExecutionData data, Dictionary <string, string> inputs, Dictionary <string, string> environment, Variables runtimeVariables, string taskDirectory) => { finialInputs = inputs; }) .Returns(new Mock <IHandler>().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 <Issue>(s => s.Message.Contains("Unexpected input 'invalid1'")), It.IsAny <string>()), Times.Once); _ec.Verify(x => x.AddIssue(It.Is <Issue>(s => s.Message.Contains("Unexpected input 'invalid2'")), It.IsAny <string>()), Times.Once); }
private void HandleMappingWithAllLooseProperties( DefinitionInfo mappingDefinition, DefinitionInfo keyDefinition, DefinitionInfo valueDefinition, MappingToken mapping) { var keys = new HashSet <String>(StringComparer.OrdinalIgnoreCase); while (m_unraveler.AllowScalar(mappingDefinition.Expand, out ScalarToken nextKeyScalar)) { // Expression if (nextKeyScalar is ExpressionToken) { if (nextKeyScalar is BasicExpressionToken) { mapping.Add(nextKeyScalar, Evaluate(valueDefinition)); } else { var anyDefinition = new DefinitionInfo(mappingDefinition, TemplateConstants.Any); mapping.Add(nextKeyScalar, Evaluate(anyDefinition)); } continue; } // Not a string if (!(nextKeyScalar is StringToken nextKey)) { nextKey = new StringToken(nextKeyScalar.FileId, nextKeyScalar.Line, nextKeyScalar.Column, nextKeyScalar.ToString()); } // Duplicate if (!keys.Add(nextKey.Value)) { m_context.Error(nextKey, TemplateStrings.ValueAlreadyDefined(nextKey.Value)); m_unraveler.SkipMappingValue(); continue; } // Validate Validate(nextKey, keyDefinition); // Add the pair var nextValue = Evaluate(valueDefinition); mapping.Add(nextKey, nextValue); } m_unraveler.ReadMappingEnd(); }
public void EvaluateExpansionOfScriptDisplayName() { // Arrange Setup(); var actionInputs = new MappingToken(null, null, null); actionInputs.Add(new StringToken(null, null, null, "script"), new BasicExpressionToken(null, null, null, "matrix.node")); var actionId = Guid.NewGuid(); var action = new Pipelines.ActionStep() { Name = "action", Id = actionId, Inputs = actionInputs, Reference = new Pipelines.ScriptReference() }; _actionRunner.Action = action; var matrixData = new DictionaryContextData { ["node"] = new StringContextData("8") }; _context.Add("matrix", matrixData); // Act // Should expand the displaynameToken and set the display name to that var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext); // Assert Assert.True(didUpdateDisplayName); Assert.Equal("Run 8", _actionRunner.DisplayName); }
private void OnDeserialized(StreamingContext context) { // todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere if (JobContainer is StringToken jobContainerStringToken) { var resourceAlias = jobContainerStringToken.Value; var resource = Resources?.Containers.SingleOrDefault(x => string.Equals(x.Alias, resourceAlias, StringComparison.OrdinalIgnoreCase)); if (resource != null) { JobContainer = ConvertToTemplateToken(resource); m_jobContainerResourceAlias = resourceAlias; } } // todo: remove after feature-flag DistributedTask.EvaluateContainerOnRunner is enabled everywhere if (m_jobSidecarContainers?.Count > 0 && (JobServiceContainers == null || JobServiceContainers.Type == TokenType.Null)) { var services = new MappingToken(null, null, null); foreach (var pair in m_jobSidecarContainers) { var networkAlias = pair.Key; var serviceResourceAlias = pair.Value; var serviceResource = Resources.Containers.Single(x => string.Equals(x.Alias, serviceResourceAlias, StringComparison.OrdinalIgnoreCase)); services.Add(new StringToken(null, null, null, networkAlias), ConvertToTemplateToken(serviceResource)); } JobServiceContainers = services; } }
public void EvaluateLegacyDisplayName() { // Arrange Setup(); var actionInputs = new MappingToken(null, null, null); actionInputs.Add(new StringToken(null, null, null, "script"), new StringToken(null, null, null, "echo hello world")); var actionId = Guid.NewGuid(); var actionDisplayName = "Run echo hello world"; var action = new Pipelines.ActionStep() { Name = "action", Id = actionId, DisplayName = actionDisplayName, Inputs = actionInputs, }; _actionRunner.Action = action; var matrixData = new DictionaryContextData { ["node"] = new NumberContextData(8) }; _context.Add("matrix", matrixData); // Act // Should not do anything if we don't have a displayNameToken to expand var didUpdateDisplayName = _actionRunner.TryEvaluateDisplayName(_context, _actionRunner.ExecutionContext); // Assert Assert.False(didUpdateDisplayName); Assert.Equal(actionDisplayName, _actionRunner.DisplayName); }
public async void MergeDefaultInputs() { //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")); var action = new Pipelines.ActionStep() { Name = "action", Id = actionId, Reference = new Pipelines.ContainerRegistryReference() { Image = "ubuntu:16.04" }, Inputs = actionInputs }; _actionRunner.Action = action; Dictionary <string, string> finialInputs = new Dictionary <string, string>(); _handlerFactory.Setup(x => x.Create(It.IsAny <IExecutionContext>(), It.IsAny <ActionStepDefinitionReference>(), It.IsAny <IStepHost>(), It.IsAny <ActionExecutionData>(), It.IsAny <Dictionary <string, string> >(), It.IsAny <Dictionary <string, string> >(), It.IsAny <Variables>(), It.IsAny <string>())) .Callback((IExecutionContext executionContext, Pipelines.ActionStepDefinitionReference actionReference, IStepHost stepHost, ActionExecutionData data, Dictionary <string, string> inputs, Dictionary <string, string> environment, Variables runtimeVariables, string taskDirectory) => { finialInputs = inputs; }) .Returns(new Mock <IHandler>().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"]); }
internal static TemplateToken ToTemplateToken(this PipelineContextData data) { if (data is null) { return(new NullToken(null, null, null)); } switch (data.Type) { case PipelineContextDataType.Dictionary: var dictionary = data.AssertDictionary("dictionary"); var mapping = new MappingToken(null, null, null); if (dictionary.Count > 0) { foreach (var pair in dictionary) { var key = new StringToken(null, null, null, pair.Key); var value = pair.Value.ToTemplateToken(); mapping.Add(key, value); } } return(mapping); case PipelineContextDataType.Array: var array = data.AssertArray("array"); var sequence = new SequenceToken(null, null, null); if (array.Count > 0) { foreach (var item in array) { sequence.Add(item.ToTemplateToken()); } } return(sequence); case PipelineContextDataType.String: var stringData = data as StringContextData; return(new StringToken(null, null, null, stringData.Value)); case PipelineContextDataType.Boolean: var booleanData = data as BooleanContextData; return(new BooleanToken(null, null, null, booleanData.Value)); case PipelineContextDataType.Number: var numberData = data as NumberContextData; return(new NumberToken(null, null, null, numberData.Value)); default: throw new NotSupportedException($"Unexpected {nameof(PipelineContextDataType)} type '{data.Type}'"); } }
public async void WriteEventPayload() { //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")); var action = new Pipelines.ActionStep() { Name = "action", Id = actionId, Reference = new Pipelines.ContainerRegistryReference() { Image = "ubuntu:16.04" }, Inputs = actionInputs }; _actionRunner.Action = action; Dictionary <string, string> finialInputs = new Dictionary <string, string>(); _handlerFactory.Setup(x => x.Create(It.IsAny <IExecutionContext>(), It.IsAny <ActionStepDefinitionReference>(), It.IsAny <IStepHost>(), It.IsAny <ActionExecutionData>(), It.IsAny <Dictionary <string, string> >(), It.IsAny <Dictionary <string, string> >(), It.IsAny <Variables>(), It.IsAny <string>())) .Callback((IExecutionContext executionContext, Pipelines.ActionStepDefinitionReference actionReference, IStepHost stepHost, ActionExecutionData data, Dictionary <string, string> inputs, Dictionary <string, string> environment, Variables runtimeVariables, string taskDirectory) => { finialInputs = inputs; }) .Returns(new Mock <IHandler>().Object); //Act await _actionRunner.RunAsync(); //Assert _ec.Verify(x => x.SetGitHubContext("event_path", Path.Combine(_hc.GetDirectory(WellKnownDirectory.Temp), "_github_workflow", "event.json")), Times.Once); }
private void HandleMappingWithWellKnownProperties( DefinitionInfo definition, List <MappingDefinition> mappingDefinitions, MappingToken mapping) { // Check if loose properties are allowed String looseKeyType = null; String looseValueType = null; DefinitionInfo?looseKeyDefinition = null; DefinitionInfo?looseValueDefinition = null; if (!String.IsNullOrEmpty(mappingDefinitions[0].LooseKeyType)) { looseKeyType = mappingDefinitions[0].LooseKeyType; looseValueType = mappingDefinitions[0].LooseValueType; } var keys = new HashSet <String>(StringComparer.OrdinalIgnoreCase); while (m_unraveler.AllowScalar(definition.Expand, out ScalarToken nextKeyScalar)) { // Expression if (nextKeyScalar is ExpressionToken) { var anyDefinition = new DefinitionInfo(definition, TemplateConstants.Any); mapping.Add(nextKeyScalar, Evaluate(anyDefinition)); continue; } // Not a string, convert if (!(nextKeyScalar is StringToken nextKey)) { nextKey = new StringToken(nextKeyScalar.FileId, nextKeyScalar.Line, nextKeyScalar.Column, nextKeyScalar.ToString()); } // Duplicate if (!keys.Add(nextKey.Value)) { m_context.Error(nextKey, TemplateStrings.ValueAlreadyDefined(nextKey.Value)); m_unraveler.SkipMappingValue(); continue; } // Well known if (m_schema.TryMatchKey(mappingDefinitions, nextKey.Value, out String nextValueType)) { var nextValueDefinition = new DefinitionInfo(definition, nextValueType); var nextValue = Evaluate(nextValueDefinition); mapping.Add(nextKey, nextValue); continue; } // Loose if (looseKeyType != null) { if (looseKeyDefinition == null) { looseKeyDefinition = new DefinitionInfo(definition, looseKeyType); looseValueDefinition = new DefinitionInfo(definition, looseValueType); } Validate(nextKey, looseKeyDefinition.Value); var nextValue = Evaluate(looseValueDefinition.Value); mapping.Add(nextKey, nextValue); continue; } // Error m_context.Error(nextKey, TemplateStrings.UnexpectedValue(nextKey.Value)); m_unraveler.SkipMappingValue(); } // Only one if (mappingDefinitions.Count > 1) { var hitCount = new Dictionary <String, Int32>(); foreach (MappingDefinition mapdef in mappingDefinitions) { foreach (String key in mapdef.Properties.Keys) { if (!hitCount.TryGetValue(key, out Int32 value)) { hitCount.Add(key, 1); } else { hitCount[key] = value + 1; } } } List <String> nonDuplicates = new List <String>(); foreach (String key in hitCount.Keys) { if (hitCount[key] == 1) { nonDuplicates.Add(key); } } nonDuplicates.Sort(); String listToDeDuplicate = String.Join(", ", nonDuplicates); m_context.Error(mapping, TemplateStrings.UnableToDetermineOneOf(listToDeDuplicate)); } m_unraveler.ReadMappingEnd(); }
private static ActionStep ConvertToStep( TemplateContext context, TemplateToken stepsItem, ReferenceNameBuilder nameBuilder) { var step = stepsItem.AssertMapping($"{PipelineTemplateConstants.Steps} item"); var continueOnError = default(ScalarToken); var env = default(TemplateToken); var id = default(StringToken); var ifCondition = default(String); var ifToken = default(ScalarToken); var name = default(ScalarToken); var run = default(ScalarToken); var timeoutMinutes = default(ScalarToken); var uses = default(StringToken); var with = default(TemplateToken); var workingDir = default(ScalarToken); var path = default(ScalarToken); var clean = default(ScalarToken); var fetchDepth = default(ScalarToken); var lfs = default(ScalarToken); var submodules = default(ScalarToken); var shell = default(ScalarToken); foreach (var stepProperty in step) { var propertyName = stepProperty.Key.AssertString($"{PipelineTemplateConstants.Steps} item key"); switch (propertyName.Value) { case PipelineTemplateConstants.Clean: clean = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Clean}"); break; case PipelineTemplateConstants.ContinueOnError: ConvertToStepContinueOnError(context, stepProperty.Value, allowExpressions: true); // Validate early if possible continueOnError = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} {PipelineTemplateConstants.ContinueOnError}"); break; case PipelineTemplateConstants.Env: ConvertToStepEnvironment(context, stepProperty.Value, StringComparer.Ordinal, allowExpressions: true); // Validate early if possible env = stepProperty.Value; break; case PipelineTemplateConstants.FetchDepth: fetchDepth = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.FetchDepth}"); break; case PipelineTemplateConstants.Id: id = stepProperty.Value.AssertString($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Id}"); if (!String.IsNullOrEmpty(id.Value)) { if (!nameBuilder.TryAddKnownName(id.Value, out var error)) { context.Error(id, error); } } break; case PipelineTemplateConstants.If: ifToken = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.If}"); break; case PipelineTemplateConstants.Lfs: lfs = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Lfs}"); break; case PipelineTemplateConstants.Name: name = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Name}"); break; case PipelineTemplateConstants.Path: path = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Path}"); break; case PipelineTemplateConstants.Run: run = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Run}"); break; case PipelineTemplateConstants.Shell: shell = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Shell}"); break; case PipelineTemplateConstants.Submodules: submodules = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Submodules}"); break; case PipelineTemplateConstants.TimeoutMinutes: ConvertToStepTimeout(context, stepProperty.Value, allowExpressions: true); // Validate early if possible timeoutMinutes = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.TimeoutMinutes}"); break; case PipelineTemplateConstants.Uses: uses = stepProperty.Value.AssertString($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Uses}"); break; case PipelineTemplateConstants.With: ConvertToStepInputs(context, stepProperty.Value, allowExpressions: true); // Validate early if possible with = stepProperty.Value; break; case PipelineTemplateConstants.WorkingDirectory: workingDir = stepProperty.Value.AssertScalar($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.WorkingDirectory}"); break; default: propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Steps} item key"); // throws break; } } // Fixup the if-condition ifCondition = ConvertToIfCondition(context, ifToken, false); if (run != null) { var result = new ActionStep { ContextName = id?.Value, ContinueOnError = continueOnError, DisplayNameToken = name, Condition = ifCondition, TimeoutInMinutes = timeoutMinutes, Environment = env, Reference = new ScriptReference(), }; var inputs = new MappingToken(null, null, null); inputs.Add(new StringToken(null, null, null, PipelineConstants.ScriptStepInputs.Script), run); if (workingDir != null) { inputs.Add(new StringToken(null, null, null, PipelineConstants.ScriptStepInputs.WorkingDirectory), workingDir); } if (shell != null) { inputs.Add(new StringToken(null, null, null, PipelineConstants.ScriptStepInputs.Shell), shell); } result.Inputs = inputs; return(result); } else { uses.AssertString($"{PipelineTemplateConstants.Steps} item {PipelineTemplateConstants.Uses}"); var result = new ActionStep { ContextName = id?.Value, ContinueOnError = continueOnError, DisplayNameToken = name, Condition = ifCondition, TimeoutInMinutes = timeoutMinutes, Inputs = with, Environment = env, }; if (uses.Value.StartsWith("docker://", StringComparison.Ordinal)) { var image = uses.Value.Substring("docker://".Length); result.Reference = new ContainerRegistryReference { Image = image }; } else if (uses.Value.StartsWith("./") || uses.Value.StartsWith(".\\")) { result.Reference = new RepositoryPathReference { RepositoryType = PipelineConstants.SelfAlias, Path = uses.Value }; } else { var usesSegments = uses.Value.Split('@'); var pathSegments = usesSegments[0].Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries); var gitRef = usesSegments.Length == 2 ? usesSegments[1] : String.Empty; if (usesSegments.Length != 2 || pathSegments.Length < 2 || String.IsNullOrEmpty(pathSegments[0]) || String.IsNullOrEmpty(pathSegments[1]) || String.IsNullOrEmpty(gitRef)) { // todo: loc context.Error(uses, $"Expected format {{org}}/{{repo}}[/path]@ref. Actual '{uses.Value}'"); } else { var repositoryName = $"{pathSegments[0]}/{pathSegments[1]}"; var directoryPath = pathSegments.Length > 2 ? String.Join("/", pathSegments.Skip(2)) : String.Empty; result.Reference = new RepositoryPathReference { RepositoryType = RepositoryTypes.GitHub, Name = repositoryName, Ref = gitRef, Path = directoryPath, }; } } return(result); } }
private void Setup([CallerMemberName] string name = "") { _ecTokenSource?.Dispose(); _ecTokenSource = new CancellationTokenSource(); // Test host context. _hc = new TestHostContext(this, name); var actionInputs = new MappingToken(null, null, null); actionInputs.Add(new StringToken(null, null, null, "input1"), new StringToken(null, null, null, "input1")); actionInputs.Add(new StringToken(null, null, null, "input2"), new StringToken(null, null, null, "")); actionInputs.Add(new StringToken(null, null, null, "input3"), new StringToken(null, null, null, "github")); var actionDefinition = new Definition() { Directory = _hc.GetDirectory(WellKnownDirectory.Work), Data = new ActionDefinitionData() { Name = name, Description = name, Inputs = actionInputs, Execution = new ScriptActionExecutionData() } }; // Mocks. _actionManager = new Mock <IActionManager>(); _actionManager.Setup(x => x.LoadAction(It.IsAny <IExecutionContext>(), It.IsAny <ActionStep>())).Returns(actionDefinition); _handlerFactory = new Mock <IHandlerFactory>(); _defaultStepHost = new Mock <IDefaultStepHost>(); _actionManifestManager = new ActionManifestManager(); _actionManifestManager.Initialize(_hc); var githubContext = new GitHubContext(); 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.ExpressionFunctions).Returns(new List <IFunctionInfo>()); _ec.Setup(x => x.IntraActionState).Returns(new Dictionary <string, string>()); _ec.Setup(x => x.EnvironmentVariables).Returns(new Dictionary <string, string>()); _ec.Setup(x => x.FileTable).Returns(new List <String>()); _ec.Setup(x => x.SetGitHubContext(It.IsAny <string>(), It.IsAny <string>())); _ec.Setup(x => x.GetGitHubContext(It.IsAny <string>())).Returns("{\"foo\":\"bar\"}"); _ec.Setup(x => x.CancellationToken).Returns(_ecTokenSource.Token); _ec.Setup(x => x.Variables).Returns(new Variables(_hc, new Dictionary <string, VariableValue>())); _ec.Setup(x => x.Write(It.IsAny <string>(), It.IsAny <string>())).Callback((string tag, string message) => { _hc.GetTrace().Info($"[{tag}]{message}"); }); _ec.Setup(x => x.AddIssue(It.IsAny <Issue>(), It.IsAny <string>())).Callback((Issue issue, string message) => { _hc.GetTrace().Info($"[{issue.Type}]{issue.Message ?? message}"); }); _hc.SetSingleton <IActionManager>(_actionManager.Object); _hc.SetSingleton <IHandlerFactory>(_handlerFactory.Object); _hc.SetSingleton <IActionManifestManager>(_actionManifestManager); _hc.EnqueueInstance <IDefaultStepHost>(_defaultStepHost.Object); // Instance to test. _actionRunner = new ActionRunner(); _actionRunner.Initialize(_hc); _actionRunner.ExecutionContext = _ec.Object; }
private void HandleMappingWithWellKnownProperties( DefinitionInfo definition, List <MappingDefinition> mappingDefinitions, MappingToken mapping) { // Check if loose properties are allowed String looseKeyType = null; String looseValueType = null; DefinitionInfo?looseKeyDefinition = null; DefinitionInfo?looseValueDefinition = null; if (!String.IsNullOrEmpty(mappingDefinitions[0].LooseKeyType)) { looseKeyType = mappingDefinitions[0].LooseKeyType; looseValueType = mappingDefinitions[0].LooseValueType; } var keys = new HashSet <String>(StringComparer.OrdinalIgnoreCase); var hasExpressionKey = false; while (m_objectReader.AllowLiteral(out LiteralToken rawLiteral)) { var nextKeyScalar = ParseScalar(rawLiteral, definition.AllowedContext); // Expression if (nextKeyScalar is ExpressionToken) { hasExpressionKey = true; // Legal if (definition.AllowedContext.Length > 0) { m_memory.AddBytes(nextKeyScalar); var anyDefinition = new DefinitionInfo(definition, TemplateConstants.Any); mapping.Add(nextKeyScalar, ReadValue(anyDefinition)); } // Illegal else { m_context.Error(nextKeyScalar, TemplateStrings.ExpressionNotAllowed()); SkipValue(); } continue; } // Not a string, convert if (!(nextKeyScalar is StringToken nextKey)) { nextKey = new StringToken(nextKeyScalar.FileId, nextKeyScalar.Line, nextKeyScalar.Column, nextKeyScalar.ToString()); } // Duplicate if (!keys.Add(nextKey.Value)) { m_context.Error(nextKey, TemplateStrings.ValueAlreadyDefined(nextKey.Value)); SkipValue(); continue; } // Well known if (m_schema.TryMatchKey(mappingDefinitions, nextKey.Value, out String nextValueType)) { m_memory.AddBytes(nextKey); var nextValueDefinition = new DefinitionInfo(definition, nextValueType); var nextValue = ReadValue(nextValueDefinition); mapping.Add(nextKey, nextValue); continue; } // Loose if (looseKeyType != null) { if (looseKeyDefinition == null) { looseKeyDefinition = new DefinitionInfo(definition, looseKeyType); looseValueDefinition = new DefinitionInfo(definition, looseValueType); } Validate(nextKey, looseKeyDefinition.Value); m_memory.AddBytes(nextKey); var nextValue = ReadValue(looseValueDefinition.Value); mapping.Add(nextKey, nextValue); continue; } // Error m_context.Error(nextKey, TemplateStrings.UnexpectedValue(nextKey.Value)); SkipValue(); } // Only one if (mappingDefinitions.Count > 1) { var hitCount = new Dictionary <String, Int32>(); foreach (MappingDefinition mapdef in mappingDefinitions) { foreach (String key in mapdef.Properties.Keys) { if (!hitCount.TryGetValue(key, out Int32 value)) { hitCount.Add(key, 1); } else { hitCount[key] = value + 1; } } } List <String> nonDuplicates = new List <String>(); foreach (String key in hitCount.Keys) { if (hitCount[key] == 1) { nonDuplicates.Add(key); } } nonDuplicates.Sort(); String listToDeDuplicate = String.Join(", ", nonDuplicates); m_context.Error(mapping, TemplateStrings.UnableToDetermineOneOf(listToDeDuplicate)); } else if (mappingDefinitions.Count == 1 && !hasExpressionKey) { foreach (var property in mappingDefinitions[0].Properties) { if (property.Value.Required) { if (!keys.Contains(property.Key)) { m_context.Error(mapping, $"Required property is missing: {property.Key}"); } } } } ExpectMappingEnd(); }