public static List <Step> ConvertToSteps( TemplateContext context, TemplateToken steps) { var stepsSequence = steps.AssertSequence($"job {PipelineTemplateConstants.Steps}"); var result = new List <Step>(); var nameBuilder = new ReferenceNameBuilder(); foreach (var stepsItem in stepsSequence) { var step = ConvertToStep(context, stepsItem, nameBuilder); if (step != null) // step = null means we are hitting error during step conversion, there should be an error in context.errors { if (step.Enabled) { result.Add(step); } } } // Generate context name if empty foreach (ActionStep step in result) { if (!String.IsNullOrEmpty(step.ContextName)) { continue; } var name = default(string); switch (step.Reference.Type) { case ActionSourceType.ContainerRegistry: var containerReference = step.Reference as ContainerRegistryReference; name = containerReference.Image; break; case ActionSourceType.Repository: var repositoryReference = step.Reference as RepositoryPathReference; name = !String.IsNullOrEmpty(repositoryReference.Name) ? repositoryReference.Name : PipelineConstants.SelfAlias; break; } if (String.IsNullOrEmpty(name)) { name = "run"; } nameBuilder.AppendSegment($"__{name}"); step.ContextName = nameBuilder.Build(); } return(result); }
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); } }