Beispiel #1
0
        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();
            }
        }
Beispiel #2
0
        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
            }
        }
Beispiel #3
0
        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
            }
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        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();
        }
Beispiel #6
0
        // 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);
        }
Beispiel #7
0
        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);
        }
Beispiel #10
0
        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"]);
        }
Beispiel #13
0
        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();
        }
Beispiel #16
0
        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;
        }
Beispiel #18
0
        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();
        }