internal static ContainerRegistryCredentials ConvertToContainerCredentials(TemplateToken token) { var credentials = token.AssertMapping(PipelineTemplateConstants.Credentials); var result = new ContainerRegistryCredentials(); foreach (var credentialProperty in credentials) { var propertyName = credentialProperty.Key.AssertString($"{PipelineTemplateConstants.Credentials} key"); switch (propertyName.Value) { case PipelineTemplateConstants.Username: result.Username = credentialProperty.Value.AssertString(PipelineTemplateConstants.Username).Value; break; case PipelineTemplateConstants.Password: result.Password = credentialProperty.Value.AssertString(PipelineTemplateConstants.Password).Value; break; default: propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Credentials} key {propertyName}"); break; } } return(result); }
public Dictionary <String, String> EvaluateJobDefaultsRun( TemplateToken token, DictionaryContextData contextData) { var result = default(Dictionary <String, String>); if (token != null && token.Type != TokenType.Null) { var context = CreateContext(contextData); try { token = TemplateEvaluator.Evaluate(context, PipelineTemplateConstants.JobDefaultsRun, token, 0, null, omitHeader: true); context.Errors.Check(); result = new Dictionary <String, String>(StringComparer.OrdinalIgnoreCase); var mapping = token.AssertMapping("defaults run"); foreach (var pair in mapping) { // Literal key var key = pair.Key.AssertString("defaults run key"); // Literal value var value = pair.Value.AssertString("defaults run value"); result[key.Value] = value.Value; } } catch (Exception ex) when(!(ex is TemplateValidationException)) { context.Errors.Add(ex); } context.Errors.Check(); } return(result); }
internal PropertyValue(TemplateToken token) { if (token is StringToken stringToken) { Type = stringToken.Value; } else { var mapping = token.AssertMapping($"{TemplateConstants.MappingPropertyValue}"); foreach (var mappingPair in mapping) { var mappingKey = mappingPair.Key.AssertString($"{TemplateConstants.MappingPropertyValue} key"); switch (mappingKey.Value) { case TemplateConstants.Type: Type = mappingPair.Value.AssertString($"{TemplateConstants.MappingPropertyValue} {TemplateConstants.Type}").Value; break; case TemplateConstants.Required: Required = mappingPair.Value.AssertBoolean($"{TemplateConstants.MappingPropertyValue} {TemplateConstants.Required}").Value; break; default: mappingKey.AssertUnexpectedValue($"{TemplateConstants.MappingPropertyValue} key"); break; } } } }
internal static Dictionary <String, String> ConvertToStepEnvironment( TemplateContext context, TemplateToken environment, StringComparer keyComparer, Boolean allowExpressions = false) { var result = new Dictionary <String, String>(keyComparer); // Expression if (allowExpressions && environment is ExpressionToken) { return(result); } // Mapping var mapping = environment.AssertMapping("environment"); foreach (var pair in mapping) { // Expression key if (allowExpressions && pair.Key is ExpressionToken) { continue; } // String key var key = pair.Key.AssertString("environment key"); // Expression value if (allowExpressions && pair.Value is ExpressionToken) { continue; } // String value var value = pair.Value.AssertString("environment value"); result[key.Value] = value.Value; } return(result); }
internal static Dictionary <String, String> ConvertToStepInputs( TemplateContext context, TemplateToken inputs, Boolean allowExpressions = false) { var result = new Dictionary <String, String>(StringComparer.OrdinalIgnoreCase); // Expression if (allowExpressions && inputs is ExpressionToken) { return(result); } // Mapping var mapping = inputs.AssertMapping("inputs"); foreach (var pair in mapping) { // Expression key if (allowExpressions && pair.Key is ExpressionToken) { continue; } // Literal key var key = pair.Key.AssertString("inputs key"); // Expression value if (allowExpressions && pair.Value is ExpressionToken) { continue; } // Literal value var value = pair.Value.AssertString("inputs value"); result[key.Value] = value.Value; } return(result); }
private void ConvertInputs( TemplateContext context, TemplateToken inputsToken, ActionDefinitionData actionDefinition) { actionDefinition.Inputs = new MappingToken(null, null, null); var inputsMapping = inputsToken.AssertMapping("inputs"); foreach (var input in inputsMapping) { bool hasDefault = false; var inputName = input.Key.AssertString("input name"); var inputMetadata = input.Value.AssertMapping("input metadata"); foreach (var metadata in inputMetadata) { var metadataName = metadata.Key.AssertString("input metadata").Value; if (string.Equals(metadataName, "default", StringComparison.OrdinalIgnoreCase)) { hasDefault = true; actionDefinition.Inputs.Add(inputName, metadata.Value); } else if (string.Equals(metadataName, "deprecationMessage", StringComparison.OrdinalIgnoreCase)) { if (actionDefinition.Deprecated == null) { actionDefinition.Deprecated = new Dictionary <String, String>(); } var message = metadata.Value.AssertString("input deprecationMessage"); actionDefinition.Deprecated.Add(inputName.Value, message.Value); } } if (!hasDefault) { actionDefinition.Inputs.Add(inputName, new StringToken(null, null, null, string.Empty)); } } }
internal static List <KeyValuePair <String, JobContainer> > ConvertToJobServiceContainers( TemplateContext context, TemplateToken services, bool allowExpressions = false) { var result = new List <KeyValuePair <String, JobContainer> >(); if (allowExpressions && services.Traverse().Any(x => x is ExpressionToken)) { return(result); } var servicesMapping = services.AssertMapping("services"); foreach (var servicePair in servicesMapping) { var networkAlias = servicePair.Key.AssertString("services key").Value; var container = ConvertToJobContainer(context, servicePair.Value); result.Add(new KeyValuePair <String, JobContainer>(networkAlias, container)); } return(result); }
private ActionExecutionData ConvertRuns( IExecutionContext executionContext, TemplateContext context, TemplateToken inputsToken, MappingToken outputs = null) { var runsMapping = inputsToken.AssertMapping("runs"); var usingToken = default(StringToken); var imageToken = default(StringToken); var argsToken = default(SequenceToken); var entrypointToken = default(StringToken); var envToken = default(MappingToken); var mainToken = default(StringToken); var pluginToken = default(StringToken); var preToken = default(StringToken); var preEntrypointToken = default(StringToken); var preIfToken = default(StringToken); var postToken = default(StringToken); var postEntrypointToken = default(StringToken); var postIfToken = default(StringToken); var stepsLoaded = default(List <Pipelines.ActionStep>); foreach (var run in runsMapping) { var runsKey = run.Key.AssertString("runs key").Value; switch (runsKey) { case "using": usingToken = run.Value.AssertString("using"); break; case "image": imageToken = run.Value.AssertString("image"); break; case "args": argsToken = run.Value.AssertSequence("args"); break; case "entrypoint": entrypointToken = run.Value.AssertString("entrypoint"); break; case "env": envToken = run.Value.AssertMapping("env"); break; case "main": mainToken = run.Value.AssertString("main"); break; case "plugin": pluginToken = run.Value.AssertString("plugin"); break; case "post": postToken = run.Value.AssertString("post"); break; case "post-entrypoint": postEntrypointToken = run.Value.AssertString("post-entrypoint"); break; case "post-if": postIfToken = run.Value.AssertString("post-if"); break; case "pre": preToken = run.Value.AssertString("pre"); break; case "pre-entrypoint": preEntrypointToken = run.Value.AssertString("pre-entrypoint"); break; case "pre-if": preIfToken = run.Value.AssertString("pre-if"); break; case "steps": if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA"))) { var steps = run.Value.AssertSequence("steps"); var evaluator = executionContext.ToPipelineTemplateEvaluator(); stepsLoaded = evaluator.LoadCompositeSteps(steps); break; } throw new Exception("You aren't supposed to be using Composite Actions yet!"); default: Trace.Info($"Ignore run property {runsKey}."); break; } } if (usingToken != null) { if (string.Equals(usingToken.Value, "docker", StringComparison.OrdinalIgnoreCase)) { if (string.IsNullOrEmpty(imageToken?.Value)) { throw new ArgumentNullException($"Image is not provided."); } else { return(new ContainerActionExecutionData() { Image = imageToken.Value, Arguments = argsToken, EntryPoint = entrypointToken?.Value, Environment = envToken, Pre = preEntrypointToken?.Value, InitCondition = preIfToken?.Value ?? "always()", Post = postEntrypointToken?.Value, CleanupCondition = postIfToken?.Value ?? "always()" }); } } else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase)) { if (string.IsNullOrEmpty(mainToken?.Value)) { throw new ArgumentNullException($"Entry javascript file is not provided."); } else { return(new NodeJSActionExecutionData() { Script = mainToken.Value, Pre = preToken?.Value, InitCondition = preIfToken?.Value ?? "always()", Post = postToken?.Value, CleanupCondition = postIfToken?.Value ?? "always()" }); } } else if (string.Equals(usingToken.Value, "composite", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA"))) { if (stepsLoaded == null) { // TODO: Add a more helpful error message + including file name, etc. to show user that it's because of their yaml file throw new ArgumentNullException($"No steps provided."); } else { return(new CompositeActionExecutionData() { Steps = stepsLoaded, Outputs = outputs }); } } else { throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker' or 'node12' instead."); } } else if (pluginToken != null) { return(new PluginActionExecutionData() { Plugin = pluginToken.Value }); } throw new NotSupportedException(nameof(ConvertRuns)); }
private ActionExecutionData ConvertRuns( IExecutionContext executionContext, TemplateContext templateContext, TemplateToken inputsToken, String fileRelativePath, MappingToken outputs = null) { var runsMapping = inputsToken.AssertMapping("runs"); var usingToken = default(StringToken); var imageToken = default(StringToken); var argsToken = default(SequenceToken); var entrypointToken = default(StringToken); var envToken = default(MappingToken); var mainToken = default(StringToken); var pluginToken = default(StringToken); var preToken = default(StringToken); var preEntrypointToken = default(StringToken); var preIfToken = default(StringToken); var postToken = default(StringToken); var postEntrypointToken = default(StringToken); var postIfToken = default(StringToken); var steps = default(List <Pipelines.Step>); foreach (var run in runsMapping) { var runsKey = run.Key.AssertString("runs key").Value; switch (runsKey) { case "using": usingToken = run.Value.AssertString("using"); break; case "image": imageToken = run.Value.AssertString("image"); break; case "args": argsToken = run.Value.AssertSequence("args"); break; case "entrypoint": entrypointToken = run.Value.AssertString("entrypoint"); break; case "env": envToken = run.Value.AssertMapping("env"); break; case "main": mainToken = run.Value.AssertString("main"); break; case "plugin": pluginToken = run.Value.AssertString("plugin"); break; case "post": postToken = run.Value.AssertString("post"); break; case "post-entrypoint": postEntrypointToken = run.Value.AssertString("post-entrypoint"); break; case "post-if": postIfToken = run.Value.AssertString("post-if"); break; case "pre": preToken = run.Value.AssertString("pre"); break; case "pre-entrypoint": preEntrypointToken = run.Value.AssertString("pre-entrypoint"); break; case "pre-if": preIfToken = run.Value.AssertString("pre-if"); break; case "steps": if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA"))) { var stepsToken = run.Value.AssertSequence("steps"); steps = PipelineTemplateConverter.ConvertToSteps(templateContext, stepsToken); templateContext.Errors.Check(); break; } throw new Exception("You aren't supposed to be using Composite Actions yet!"); default: Trace.Info($"Ignore run property {runsKey}."); break; } } if (usingToken != null) { if (string.Equals(usingToken.Value, "docker", StringComparison.OrdinalIgnoreCase)) { if (string.IsNullOrEmpty(imageToken?.Value)) { throw new ArgumentNullException($"You are using a Container Action but an image is not provided in {fileRelativePath}."); } else { return(new ContainerActionExecutionData() { Image = imageToken.Value, Arguments = argsToken, EntryPoint = entrypointToken?.Value, Environment = envToken, Pre = preEntrypointToken?.Value, InitCondition = preIfToken?.Value ?? "always()", Post = postEntrypointToken?.Value, CleanupCondition = postIfToken?.Value ?? "always()" }); } } else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase)) { if (string.IsNullOrEmpty(mainToken?.Value)) { throw new ArgumentNullException($"You are using a JavaScript Action but there is not an entry JavaScript file provided in {fileRelativePath}."); } else { return(new NodeJSActionExecutionData() { Script = mainToken.Value, Pre = preToken?.Value, InitCondition = preIfToken?.Value ?? "always()", Post = postToken?.Value, CleanupCondition = postIfToken?.Value ?? "always()" }); } } else if (string.Equals(usingToken.Value, "composite", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("TESTING_COMPOSITE_ACTIONS_ALPHA"))) { if (steps == null) { throw new ArgumentNullException($"You are using a composite action but there are no steps provided in {fileRelativePath}."); } else { return(new CompositeActionExecutionData() { Steps = steps.Cast <Pipelines.ActionStep>().ToList(), Outputs = outputs }); } } else { throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker' or 'node12' instead."); } } else if (pluginToken != null) { return(new PluginActionExecutionData() { Plugin = pluginToken.Value }); } throw new NotSupportedException(nameof(ConvertRuns)); }
private ActionExecutionData ConvertRuns( TemplateContext context, TemplateToken inputsToken) { var runsMapping = inputsToken.AssertMapping("runs"); var usingToken = default(StringToken); var imageToken = default(StringToken); var argsToken = default(SequenceToken); var entrypointToken = default(StringToken); var envToken = default(MappingToken); var mainToken = default(StringToken); var pluginToken = default(StringToken); var postToken = default(StringToken); var postEntrypointToken = default(StringToken); var postIfToken = default(StringToken); foreach (var run in runsMapping) { var runsKey = run.Key.AssertString("runs key").Value; switch (runsKey) { case "using": usingToken = run.Value.AssertString("using"); break; case "image": imageToken = run.Value.AssertString("image"); break; case "args": argsToken = run.Value.AssertSequence("args"); break; case "entrypoint": entrypointToken = run.Value.AssertString("entrypoint"); break; case "env": envToken = run.Value.AssertMapping("env"); break; case "main": mainToken = run.Value.AssertString("main"); break; case "plugin": pluginToken = run.Value.AssertString("plugin"); break; case "post": postToken = run.Value.AssertString("post"); break; case "post-entrypoint": postEntrypointToken = run.Value.AssertString("post-entrypoint"); break; case "post-if": postIfToken = run.Value.AssertString("post-if"); break; default: Trace.Info($"Ignore run property {runsKey}."); break; } } if (usingToken != null) { if (string.Equals(usingToken.Value, "docker", StringComparison.OrdinalIgnoreCase)) { if (string.IsNullOrEmpty(imageToken?.Value)) { throw new ArgumentNullException($"Image is not provided."); } else { return(new ContainerActionExecutionData() { Image = imageToken.Value, Arguments = argsToken, EntryPoint = entrypointToken?.Value, Environment = envToken, Cleanup = postEntrypointToken?.Value, CleanupCondition = postIfToken?.Value }); } } else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase)) { if (string.IsNullOrEmpty(mainToken?.Value)) { throw new ArgumentNullException($"Entry javascript fils is not provided."); } else { return(new NodeJSActionExecutionData() { Script = mainToken.Value, Cleanup = postToken?.Value, CleanupCondition = postIfToken?.Value }); } } else { throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker' or 'node12' instead."); } } else if (pluginToken != null) { return(new PluginActionExecutionData() { Plugin = pluginToken.Value }); } throw new NotSupportedException(nameof(ConvertRuns)); }
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); } }
internal static JobContainer ConvertToJobContainer( TemplateContext context, TemplateToken value, bool allowExpressions = false) { var result = new JobContainer(); if (allowExpressions && value.Traverse().Any(x => x is ExpressionToken)) { return(result); } if (value is StringToken containerLiteral) { if (String.IsNullOrEmpty(containerLiteral.Value)) { return(null); } result.Image = containerLiteral.Value; } else { var containerMapping = value.AssertMapping($"{PipelineTemplateConstants.Container}"); foreach (var containerPropertyPair in containerMapping) { var propertyName = containerPropertyPair.Key.AssertString($"{PipelineTemplateConstants.Container} key"); switch (propertyName.Value) { case PipelineTemplateConstants.Image: result.Image = containerPropertyPair.Value.AssertString($"{PipelineTemplateConstants.Container} {propertyName}").Value; break; case PipelineTemplateConstants.Env: var env = containerPropertyPair.Value.AssertMapping($"{PipelineTemplateConstants.Container} {propertyName}"); var envDict = new Dictionary <String, String>(env.Count); foreach (var envPair in env) { var envKey = envPair.Key.ToString(); var envValue = envPair.Value.AssertString($"{PipelineTemplateConstants.Container} {propertyName} {envPair.Key.ToString()}").Value; envDict.Add(envKey, envValue); } result.Environment = envDict; break; case PipelineTemplateConstants.Options: result.Options = containerPropertyPair.Value.AssertString($"{PipelineTemplateConstants.Container} {propertyName}").Value; break; case PipelineTemplateConstants.Ports: var ports = containerPropertyPair.Value.AssertSequence($"{PipelineTemplateConstants.Container} {propertyName}"); var portList = new List <String>(ports.Count); foreach (var portItem in ports) { var portString = portItem.AssertString($"{PipelineTemplateConstants.Container} {propertyName} {portItem.ToString()}").Value; portList.Add(portString); } result.Ports = portList; break; case PipelineTemplateConstants.Volumes: var volumes = containerPropertyPair.Value.AssertSequence($"{PipelineTemplateConstants.Container} {propertyName}"); var volumeList = new List <String>(volumes.Count); foreach (var volumeItem in volumes) { var volumeString = volumeItem.AssertString($"{PipelineTemplateConstants.Container} {propertyName} {volumeItem.ToString()}").Value; volumeList.Add(volumeString); } result.Volumes = volumeList; break; case PipelineTemplateConstants.Credentials: result.Credentials = ConvertToContainerCredentials(containerPropertyPair.Value); break; default: propertyName.AssertUnexpectedValue($"{PipelineTemplateConstants.Container} key"); break; } } } if (result.Image.StartsWith("docker://", StringComparison.Ordinal)) { result.Image = result.Image.Substring("docker://".Length); } if (String.IsNullOrEmpty(result.Image)) { context.Error(value, "Container image cannot be empty"); } return(result); }