コード例 #1
0
        public void GitHubDeserializationTest()
        {
            //Arrange
            string yaml = @"
on:
  push:
    branches:
    - master
jobs:
  build:
    runs-on: ubuntu-latest
    name: Build 1
    runs-on: windows-latest
    steps:
    - uses: actions/checkout@v2
    - run: Write-Host ""Hello world!""
      shell: powershell
";

            //Act
            GitHubActionsRoot gitHubAction = GitHubActionsSerialization.Deserialize(yaml);

            //Assert
            Assert.AreNotEqual(null, gitHubAction);
            Assert.AreEqual(null, gitHubAction.env); //environment variables are null

            //Test for messages and name
            Assert.AreEqual(0, gitHubAction.messages.Count);
            Assert.AreEqual(null, gitHubAction.name);

            //Test the trigger
            Assert.AreNotEqual(null, gitHubAction.on);
            Assert.AreEqual(null, gitHubAction.on.pull_request);
            Assert.AreNotEqual(null, gitHubAction.on.push);
            Assert.AreEqual(1, gitHubAction.on.push.branches.Length);
            Assert.AreEqual("master", gitHubAction.on.push.branches[0]);
            Assert.AreEqual(null, gitHubAction.on.push.branches_ignore);
            Assert.AreEqual(null, gitHubAction.on.push.paths);
            Assert.AreEqual(null, gitHubAction.on.push.paths_ignore);
            Assert.AreEqual(null, gitHubAction.on.push.tags);
            Assert.AreEqual(null, gitHubAction.on.push.tags_ignore);
            Assert.AreEqual(null, gitHubAction.on.schedule);

            //Test that jobs exist
            Assert.AreNotEqual(null, gitHubAction.jobs);
            Assert.AreEqual(1, gitHubAction.jobs.Count);
            Assert.AreEqual(true, gitHubAction.jobs.ContainsKey("build"));
            gitHubAction.jobs.TryGetValue("build", out Job gitHubJob);
            Assert.AreEqual(null, gitHubJob._if);
            Assert.AreEqual("Build 1", gitHubJob.name);
            Assert.AreEqual("windows-latest", gitHubJob.runs_on);

            //Test that steps exist
            Assert.AreNotEqual(null, gitHubJob.steps);
            Assert.AreEqual(2, gitHubJob.steps.Length);
        }
コード例 #2
0
        /// <summary>
        /// Convert a single Azure DevOps Pipeline task to a GitHub Actions task
        /// </summary>
        /// <param name="input">Yaml to convert</param>
        /// <returns>Converion object, with original yaml, processed yaml, and comments on the conversion</returns>
        public ConversionResponse ConvertAzurePipelineTaskToGitHubActionTask(string input)
        {
            string yaml           = "";
            string processedInput = ConversionUtility.StepsPreProcessing(input);

            GitHubActions.Step gitHubActionStep = new GitHubActions.Step();

            //Process the YAML for the individual job
            AzurePipelines.Job azurePipelinesJob = GenericObjectSerialization.DeserializeYaml <AzurePipelines.Job>(processedInput);
            if (azurePipelinesJob != null && azurePipelinesJob.steps != null && azurePipelinesJob.steps.Length > 0)
            {
                //As we needed to create an entire (but minimal) pipelines job, we need to now extract the step for processing
                StepsProcessing stepsProcessing = new StepsProcessing();
                gitHubActionStep = stepsProcessing.ProcessStep(azurePipelinesJob.steps[0]);

                //Find all variables in this text block, we need this for a bit later
                VariablesProcessing vp           = new VariablesProcessing(_verbose);
                List <string>       variableList = vp.SearchForVariables(processedInput);

                //Create the GitHub YAML and apply some adjustments
                if (gitHubActionStep != null)
                {
                    //add the step into a github job so it renders correctly
                    GitHubActions.Job gitHubJob = new GitHubActions.Job
                    {
                        steps = new GitHubActions.Step[1] //create an array of size 1
                    };
                    //Load the step into the single item array
                    gitHubJob.steps[0] = gitHubActionStep;

                    //Finally, we can serialize the job back to yaml
                    yaml = GitHubActionsSerialization.SerializeJob(gitHubJob, variableList);
                }
            }

            //Load failed tasks and comments for processing
            List <string> allComments = new List <string>();

            if (gitHubActionStep != null)
            {
                allComments.Add(gitHubActionStep.step_message);
            }

            //Return the final conversion result, with the original (pipeline) yaml, processed (actions) yaml, and any comments
            return(new ConversionResponse
            {
                pipelinesYaml = input,
                actionsYaml = yaml,
                comments = allComments
            });
        }
コード例 #3
0
        /// <summary>
        /// Convert an entire Azure DevOps Pipeline to a GitHub Actions
        /// </summary>
        /// <param name="input">Yaml to convert</param>
        /// <returns>Converion object, with original yaml, processed yaml, and comments on the conversion</returns>
        public ConversionResponse ConvertAzurePipelineToGitHubAction(string input)
        {
            List <string>     variableList = new List <string>();
            string            yaml;
            GitHubActionsRoot gitHubActions = null;

            //Triggers and variables are hard, as there are two data types for each that can exist, so we need to go with the most common type and handle the less common type with exceptions.
            //There are 4 combinations here, simple/simple, simple/complex, complex/simple, and complex/complex
            AzurePipelinesRoot <string[], Dictionary <string, string> > azurePipelineWithSimpleTriggerAndSimpleVariables  = null;
            AzurePipelinesRoot <string[], AzurePipelines.Variable[]>    azurePipelineWithSimpleTriggerAndComplexVariables = null;
            AzurePipelinesRoot <AzurePipelines.Trigger, Dictionary <string, string> > azurePipelineWithComplexTriggerAndSimpleVariables  = null;
            AzurePipelinesRoot <AzurePipelines.Trigger, AzurePipelines.Variable[]>    azurePipelineWithComplexTriggerAndComplexVariables = null;

            try
            {
                azurePipelineWithSimpleTriggerAndSimpleVariables = AzurePipelinesSerialization <string[], Dictionary <string, string> > .DeserializeSimpleTriggerAndSimpleVariables(input);
            }
            catch
            {
                try
                {
                    azurePipelineWithComplexTriggerAndSimpleVariables = AzurePipelinesSerialization <AzurePipelines.Trigger, Dictionary <string, string> > .DeserializeComplexTriggerAndSimpleVariables(input);
                }
                catch
                {
                    try
                    {
                        azurePipelineWithSimpleTriggerAndComplexVariables = AzurePipelinesSerialization <string[], AzurePipelines.Variable[]> .DeserializeSimpleTriggerAndComplexVariables(input);
                    }
                    catch
                    {
                        azurePipelineWithComplexTriggerAndComplexVariables = AzurePipelinesSerialization <AzurePipelines.Trigger, AzurePipelines.Variable[]> .DeserializeComplexTriggerAndComplexVariables(input);
                    }
                }
            }
            //Generate the github actions
            if (azurePipelineWithSimpleTriggerAndSimpleVariables != null)
            {
                PipelineProcessing <string[], Dictionary <string, string> > processing = new PipelineProcessing <string[], Dictionary <string, string> >();
                gitHubActions = processing.ProcessPipeline(azurePipelineWithSimpleTriggerAndSimpleVariables, azurePipelineWithSimpleTriggerAndSimpleVariables.trigger, null, azurePipelineWithSimpleTriggerAndSimpleVariables.variables, null);
                if (processing.MatrixVariableName != null)
                {
                    _matrixVariableName = processing.MatrixVariableName;
                }
                variableList.AddRange(processing.VariableList);
            }
            else if (azurePipelineWithSimpleTriggerAndComplexVariables != null)
            {
                PipelineProcessing <string[], AzurePipelines.Variable[]> processing = new PipelineProcessing <string[], AzurePipelines.Variable[]>();
                gitHubActions = processing.ProcessPipeline(azurePipelineWithSimpleTriggerAndComplexVariables, azurePipelineWithSimpleTriggerAndComplexVariables.trigger, null, null, azurePipelineWithSimpleTriggerAndComplexVariables.variables);
                if (processing.MatrixVariableName != null)
                {
                    _matrixVariableName = processing.MatrixVariableName;
                }
                variableList.AddRange(processing.VariableList);
            }
            else if (azurePipelineWithComplexTriggerAndSimpleVariables != null)
            {
                PipelineProcessing <AzurePipelines.Trigger, Dictionary <string, string> > processing = new PipelineProcessing <AzurePipelines.Trigger, Dictionary <string, string> >();
                gitHubActions = processing.ProcessPipeline(azurePipelineWithComplexTriggerAndSimpleVariables, null, azurePipelineWithComplexTriggerAndSimpleVariables.trigger, azurePipelineWithComplexTriggerAndSimpleVariables.variables, null);
                if (processing.MatrixVariableName != null)
                {
                    _matrixVariableName = processing.MatrixVariableName;
                }
                variableList.AddRange(processing.VariableList);
            }
            else if (azurePipelineWithComplexTriggerAndComplexVariables != null)
            {
                PipelineProcessing <AzurePipelines.Trigger, AzurePipelines.Variable[]> processing = new PipelineProcessing <AzurePipelines.Trigger, AzurePipelines.Variable[]>();
                gitHubActions = processing.ProcessPipeline(azurePipelineWithComplexTriggerAndComplexVariables, null, azurePipelineWithComplexTriggerAndComplexVariables.trigger, null, azurePipelineWithComplexTriggerAndComplexVariables.variables);
                if (processing.MatrixVariableName != null)
                {
                    _matrixVariableName = processing.MatrixVariableName;
                }
                variableList.AddRange(processing.VariableList);
            }

            //Commented out the new solution, as it doesn't process failed/invalid documents.
            //var success = false;
            //if (!success)
            //{
            //    var azurePipelineWithSimpleTriggerAndSimpleVariables = AzurePipelinesSerialization<string[], Dictionary<string, string>>.DeserializeSimpleTriggerAndSimpleVariables(input);
            //    if (azurePipelineWithSimpleTriggerAndSimpleVariables != null)
            //    {
            //        success = true;
            //        PipelineProcessing<string[], Dictionary<string, string>> processing = new PipelineProcessing<string[], Dictionary<string, string>>();
            //        gitHubActions = processing.ProcessPipeline(azurePipelineWithSimpleTriggerAndSimpleVariables, azurePipelineWithSimpleTriggerAndSimpleVariables.trigger, null, azurePipelineWithSimpleTriggerAndSimpleVariables.variables, null);
            //        if (processing.MatrixVariableName != null)
            //        {
            //            _matrixVariableName = processing.MatrixVariableName;
            //        }
            //        variableList.AddRange(processing.VariableList);
            //    }
            //}

            //if (!success)
            //{
            //    var azurePipelineWithSimpleTriggerAndComplexVariables = AzurePipelinesSerialization<string[], AzurePipelines.Variable[]>.DeserializeSimpleTriggerAndComplexVariables(input);
            //    if (azurePipelineWithSimpleTriggerAndComplexVariables != null)
            //    {
            //        success = true;
            //        PipelineProcessing<string[], AzurePipelines.Variable[]> processing = new PipelineProcessing<string[], AzurePipelines.Variable[]>();
            //        gitHubActions = processing.ProcessPipeline(azurePipelineWithSimpleTriggerAndComplexVariables, azurePipelineWithSimpleTriggerAndComplexVariables.trigger, null, null, azurePipelineWithSimpleTriggerAndComplexVariables.variables);
            //        if (processing.MatrixVariableName != null)
            //        {
            //            _matrixVariableName = processing.MatrixVariableName;
            //        }
            //        variableList.AddRange(processing.VariableList);
            //    }
            //}

            //if (!success)
            //{
            //    var azurePipelineWithComplexTriggerAndSimpleVariables = AzurePipelinesSerialization<AzurePipelines.Trigger, Dictionary<string, string>>.DeserializeComplexTriggerAndSimpleVariables(input);
            //    if (azurePipelineWithComplexTriggerAndSimpleVariables != null)
            //    {
            //        success = true;
            //        PipelineProcessing<AzurePipelines.Trigger, Dictionary<string, string>> processing = new PipelineProcessing<AzurePipelines.Trigger, Dictionary<string, string>>();
            //        gitHubActions = processing.ProcessPipeline(azurePipelineWithComplexTriggerAndSimpleVariables, null, azurePipelineWithComplexTriggerAndSimpleVariables.trigger, azurePipelineWithComplexTriggerAndSimpleVariables.variables, null);
            //        if (processing.MatrixVariableName != null)
            //        {
            //            _matrixVariableName = processing.MatrixVariableName;
            //        }
            //        variableList.AddRange(processing.VariableList);
            //    }
            //}

            //if (!success)
            //{
            //    var azurePipelineWithComplexTriggerAndComplexVariables = AzurePipelinesSerialization<AzurePipelines.Trigger, AzurePipelines.Variable[]>.DeserializeComplexTriggerAndComplexVariables(input);
            //    if (azurePipelineWithComplexTriggerAndComplexVariables != null)
            //    {
            //        PipelineProcessing<AzurePipelines.Trigger, AzurePipelines.Variable[]> processing = new PipelineProcessing<AzurePipelines.Trigger, AzurePipelines.Variable[]>();
            //        gitHubActions = processing.ProcessPipeline(azurePipelineWithComplexTriggerAndComplexVariables, null, azurePipelineWithComplexTriggerAndComplexVariables.trigger, null, azurePipelineWithComplexTriggerAndComplexVariables.variables);
            //        if (processing.MatrixVariableName != null)
            //        {
            //            _matrixVariableName = processing.MatrixVariableName;
            //        }
            //        variableList.AddRange(processing.VariableList);
            //    }
            //}
            //if (!success)
            //{
            //    throw new NotSupportedException("All deserialisation methods failed... oops! Please create a GitHub issue so we can fix this");
            //}

            //Search for any other variables. Duplicates are ok, they are processed the same
            variableList.AddRange(SearchForVariables(input));

            //Create the YAML and apply some adjustments
            if (gitHubActions != null)
            {
                yaml = GitHubActionsSerialization.Serialize(gitHubActions, variableList, _matrixVariableName);
            }
            else
            {
                yaml = "";
            }

            //Load failed task comments for processing
            List <string> stepComments = new List <string>();

            if (gitHubActions != null)
            {
                //Add any header messages
                if (gitHubActions.messages != null)
                {
                    foreach (string message in gitHubActions.messages)
                    {
                        stepComments.Add(ConvertMessageToYamlComment(message));
                    }
                }
                if (gitHubActions.jobs != null)
                {
                    //Add each individual step comments
                    foreach (KeyValuePair <string, GitHubActions.Job> job in gitHubActions.jobs)
                    {
                        if (job.Value.steps != null)
                        {
                            if (job.Value.job_message != null)
                            {
                                stepComments.Add(ConvertMessageToYamlComment(job.Value.job_message));
                            }
                            foreach (GitHubActions.Step step in job.Value.steps)
                            {
                                if (step != null && string.IsNullOrEmpty(step.step_message) == false)
                                {
                                    stepComments.Add(ConvertMessageToYamlComment(step.step_message));
                                }
                            }
                        }
                    }
                }
            }

            //Append all of the comments to the top of the file
            foreach (string item in stepComments)
            {
                yaml = item + Environment.NewLine + yaml;
            }

            //Return the final conversion result, with the original (pipeline) yaml, processed (actions) yaml, and any comments
            return(new ConversionResponse
            {
                pipelinesYaml = input,
                actionsYaml = yaml,
                comments = stepComments
            });
        }
コード例 #4
0
        /// <summary>
        /// V1 plan:
        /// 1. get the yaml
        /// 2. try to deserialize the entire doc on a few common combinations
        /// 3. convert the azure pipelines objects into a github action
        /// </summary>
        /// <param name="yaml"></param>
        /// <returns></returns>
        private ConversionResponse ConvertAzurePipelineToGitHubActionV1(string yaml)
        {
            string            gitHubYaml;
            List <string>     variableList  = new List <string>();
            List <string>     stepComments  = new List <string>();
            GitHubActionsRoot gitHubActions = null;

            //Run some processing to convert simple pools and demands to the complex editions, to avoid adding to the combinations below.
            //Also clean and remove variables with reserved words that get into trouble during deserialization. HACK alert... :(
            string processedInput = ConversionUtility.CleanYamlBeforeDeserialization(yaml);

            //Start the main deserialization methods
            bool success = false;

            if (success == false)
            {
                var azurePipelineWithSimpleTriggerAndSimpleVariables = AzurePipelinesSerialization <string[], Dictionary <string, string> > .DeserializeSimpleTriggerAndSimpleVariables(processedInput);

                if (azurePipelineWithSimpleTriggerAndSimpleVariables != null)
                {
                    success = true;
                    var pp = new PipelineProcessing <string[], Dictionary <string, string> >(_verbose);
                    gitHubActions = pp.ProcessPipeline(azurePipelineWithSimpleTriggerAndSimpleVariables, azurePipelineWithSimpleTriggerAndSimpleVariables.trigger, null, azurePipelineWithSimpleTriggerAndSimpleVariables.variables, null);
                    if (pp.MatrixVariableName != null)
                    {
                        _matrixVariableName = pp.MatrixVariableName;
                    }
                    variableList.AddRange(pp.VariableList);
                }
            }

            if (success == false)
            {
                var azurePipelineWithSimpleTriggerAndComplexVariables = AzurePipelinesSerialization <string[], AzurePipelines.Variable[]> .DeserializeSimpleTriggerAndComplexVariables(processedInput);

                if (azurePipelineWithSimpleTriggerAndComplexVariables != null)
                {
                    success = true;
                    var pp = new PipelineProcessing <string[], AzurePipelines.Variable[]>(_verbose);
                    gitHubActions = pp.ProcessPipeline(azurePipelineWithSimpleTriggerAndComplexVariables, azurePipelineWithSimpleTriggerAndComplexVariables.trigger, null, null, azurePipelineWithSimpleTriggerAndComplexVariables.variables);
                    if (pp.MatrixVariableName != null)
                    {
                        _matrixVariableName = pp.MatrixVariableName;
                    }
                    variableList.AddRange(pp.VariableList);
                }
            }

            if (success == false)
            {
                var azurePipelineWithComplexTriggerAndSimpleVariables = AzurePipelinesSerialization <AzurePipelines.Trigger, Dictionary <string, string> > .DeserializeComplexTriggerAndSimpleVariables(processedInput);

                if (azurePipelineWithComplexTriggerAndSimpleVariables != null)
                {
                    success = true;
                    var pp = new PipelineProcessing <AzurePipelines.Trigger, Dictionary <string, string> >(_verbose);
                    gitHubActions = pp.ProcessPipeline(azurePipelineWithComplexTriggerAndSimpleVariables, null, azurePipelineWithComplexTriggerAndSimpleVariables.trigger, azurePipelineWithComplexTriggerAndSimpleVariables.variables, null);
                    if (pp.MatrixVariableName != null)
                    {
                        _matrixVariableName = pp.MatrixVariableName;
                    }
                    variableList.AddRange(pp.VariableList);
                }
            }

            if (success == false)
            {
                var azurePipelineWithComplexTriggerAndComplexVariables = AzurePipelinesSerialization <AzurePipelines.Trigger, AzurePipelines.Variable[]> .DeserializeComplexTriggerAndComplexVariables(processedInput);

                if (azurePipelineWithComplexTriggerAndComplexVariables != null)
                {
                    success = true;
                    var pp = new PipelineProcessing <AzurePipelines.Trigger, AzurePipelines.Variable[]>(_verbose);
                    gitHubActions = pp.ProcessPipeline(azurePipelineWithComplexTriggerAndComplexVariables, null, azurePipelineWithComplexTriggerAndComplexVariables.trigger, null, azurePipelineWithComplexTriggerAndComplexVariables.variables);
                    if (pp.MatrixVariableName != null)
                    {
                        _matrixVariableName = pp.MatrixVariableName;
                    }
                    variableList.AddRange(pp.VariableList);
                }
            }
            if (success == false && string.IsNullOrEmpty(processedInput?.Trim()) == false)
            {
                throw new NotSupportedException("All deserialisation methods failed... oops! Please create a GitHub issue so we can fix this");
            }

            //Search for any other variables. Duplicates are ok, they are processed the same
            VariablesProcessing vp = new VariablesProcessing(_verbose);

            variableList.AddRange(vp.SearchForVariables(processedInput));

            //Create the GitHub YAML and apply some adjustments
            if (gitHubActions != null)
            {
                gitHubYaml = GitHubActionsSerialization.Serialize(gitHubActions, variableList, _matrixVariableName);
            }
            else
            {
                gitHubYaml = "";
            }

            //Load failed task comments for processing
            if (gitHubActions != null)
            {
                //Add any header messages
                if (gitHubActions.messages != null)
                {
                    foreach (string message in gitHubActions.messages)
                    {
                        stepComments.Add(ConversionUtility.ConvertMessageToYamlComment(message));
                    }
                }
                if (gitHubActions.jobs != null)
                {
                    //Add each individual step comments
                    foreach (KeyValuePair <string, GitHubActions.Job> job in gitHubActions.jobs)
                    {
                        if (job.Value.steps != null)
                        {
                            if (job.Value.job_message != null)
                            {
                                stepComments.Add(ConversionUtility.ConvertMessageToYamlComment(job.Value.job_message));
                            }
                            foreach (GitHubActions.Step step in job.Value.steps)
                            {
                                if (step != null && string.IsNullOrEmpty(step.step_message) == false)
                                {
                                    stepComments.Add(ConversionUtility.ConvertMessageToYamlComment(step.step_message));
                                }
                            }
                        }
                    }
                }
            }

            //Append all of the comments to the top of the file
            foreach (string item in stepComments)
            {
                gitHubYaml = item + System.Environment.NewLine + gitHubYaml;
            }

            //Return the final conversion result, with the original (pipeline) yaml, processed (actions) yaml, and any comments
            return(new ConversionResponse
            {
                pipelinesYaml = yaml,
                actionsYaml = gitHubYaml,
                comments = stepComments,
                v2ConversionSuccessful = false
            });
        }
コード例 #5
0
        /// <summary>
        /// V2 plan:
        /// 1. get the yaml
        /// 2. converting the yaml into a json document
        /// 3. parse the json document into azure pipelines sub-objects
        /// 4. put it together into one azure pipelines object
        /// 5. convert the azure pipelines object to github action
        /// </summary>
        /// <param name="yaml"></param>
        /// <returns></returns>

        private ConversionResponse ConvertAzurePipelineToGitHubActionV2(string yaml)
        {
            string        gitHubYaml   = "";
            List <string> variableList = new List <string>();
            List <string> stepComments = new List <string>();

            //convert the yaml into json, it's easier to parse
            JObject json = null;

            if (yaml != null)
            {
                //Clean up the YAML to remove conditional insert statements
                string processedYaml = ConversionUtility.CleanYamlBeforeDeserializationV2(yaml);
                json = JSONSerialization.DeserializeStringToObject(processedYaml);
            }

            //Build up the GitHub object piece by piece
            GitHubActionsRoot gitHubActions = new GitHubActionsRoot();
            GeneralProcessing gp            = new GeneralProcessing(_verbose);

            if (json != null)
            {
                //Name
                if (json["name"] != null)
                {
                    string nameYaml = json["name"].ToString();
                    gitHubActions.name = gp.ProcessNameV2(nameYaml);
                }

                //Trigger/PR/Schedules
                TriggerProcessing tp = new TriggerProcessing(_verbose);
                if (json["trigger"] != null)
                {
                    string triggerYaml = json["trigger"].ToString();
                    triggerYaml      = ConversionUtility.ProcessNoneJsonElement(triggerYaml);
                    gitHubActions.on = tp.ProcessTriggerV2(triggerYaml);
                }
                if (json["pr"] != null)
                {
                    string prYaml = json["pr"].ToString();
                    prYaml = ConversionUtility.ProcessNoneJsonElement(prYaml);
                    GitHubActions.Trigger prTrigger = tp.ProcessPullRequestV2(prYaml);
                    if (gitHubActions.on == null)
                    {
                        gitHubActions.on = prTrigger;
                    }
                    else
                    {
                        gitHubActions.on.pull_request = prTrigger.pull_request;
                    }
                }
                if (json["schedules"] != null)
                {
                    string schedulesYaml            = json["schedules"].ToString();
                    GitHubActions.Trigger schedules = tp.ProcessSchedulesV2(schedulesYaml);
                    if (gitHubActions.on == null)
                    {
                        gitHubActions.on = schedules;
                    }
                    else
                    {
                        gitHubActions.on.schedule = schedules.schedule;
                    }
                }

                //Parameters & Variables
                string parametersYaml  = json["parameters"]?.ToString();
                string variablesYaml   = json["variables"]?.ToString();
                VariablesProcessing vp = new VariablesProcessing(_verbose);
                gitHubActions.env = vp.ProcessParametersAndVariablesV2(parametersYaml, variablesYaml);

                //Resources
                string resourcesYaml = json["resources"]?.ToString();
                //Resource Pipelines
                if (resourcesYaml?.IndexOf("\"pipelines\"") >= 0)
                {
                    gitHubActions.messages.Add("TODO: Resource pipelines conversion not yet done: https://github.com/samsmithnz/AzurePipelinesToGitHubActionsConverter/issues/8");
                }
                //Resource Repositories
                if (resourcesYaml?.IndexOf("\"repositories\"") >= 0)
                {
                    gitHubActions.messages.Add("TODO: Resource repositories conversion not yet done: https://github.com/samsmithnz/AzurePipelinesToGitHubActionsConverter/issues/8");
                }
                //Resource Container
                if (resourcesYaml?.IndexOf("\"containers\"") >= 0)
                {
                    gitHubActions.messages.Add("TODO: Container conversion not yet done, we need help!: https://github.com/samsmithnz/AzurePipelinesToGitHubActionsConverter/issues/39");
                }
                //Strategy
                string strategyYaml = json["strategy"]?.ToString();


                //If we have stages, convert them into jobs first:
                if (json["stages"] != null)
                {
                    StagesProcessing sp = new StagesProcessing(_verbose);
                    gitHubActions.jobs = sp.ProcessStagesV2(json["stages"], strategyYaml);
                }
                //If we don't have stages, but have jobs:
                else if (json["stages"] == null && json["jobs"] != null)
                {
                    JobProcessing jp = new JobProcessing(_verbose);
                    gitHubActions.jobs  = jp.ProcessJobsV2(jp.ExtractAzurePipelinesJobsV2(json["jobs"], strategyYaml), gp.ExtractResourcesV2(resourcesYaml));
                    _matrixVariableName = jp.MatrixVariableName;
                }
                //Otherwise, if we don't have stages or jobs, we just have steps, and need to load them into a new job
                else if (json["stages"] == null && json["jobs"] == null)
                {
                    //Pool
                    string poolYaml = json["pool"]?.ToString();
                    //pool/demands
                    if (poolYaml?.IndexOf("\"demands\":") >= 0)
                    {
                        gitHubActions.messages.Add("Note: GitHub Actions does not have a 'demands' command on 'runs-on' yet");
                    }

                    //Steps
                    string               stepsYaml    = json["steps"]?.ToString();
                    JobProcessing        jp           = new JobProcessing(_verbose);
                    AzurePipelines.Job[] pipelineJobs = jp.ProcessJobFromPipelineRootV2(poolYaml, strategyYaml, stepsYaml);
                    Resources            resources    = gp.ExtractResourcesV2(resourcesYaml);
                    gitHubActions.jobs  = jp.ProcessJobsV2(pipelineJobs, resources);
                    _matrixVariableName = jp.MatrixVariableName;
                }

                if (gitHubActions.jobs != null && gitHubActions.jobs.Count == 0)
                {
                    gitHubActions.messages.Add("Note that although having no jobs is valid YAML, it is not a valid GitHub Action.");
                }

                //Load in all variables. Duplicates are ok, they are processed the same
                variableList.AddRange(vp.SearchForVariables(yaml));
                variableList.AddRange(vp.SearchForVariablesV2(gitHubActions));

                //Create the GitHub YAML and apply some adjustments
                if (gitHubActions != null)
                {
                    gitHubYaml = GitHubActionsSerialization.Serialize(gitHubActions, variableList, _matrixVariableName);
                }
                else
                {
                    gitHubYaml = "";
                }

                //Load failed task comments for processing
                //Add any header messages
                if (gitHubActions?.messages != null)
                {
                    foreach (string message in gitHubActions.messages)
                    {
                        stepComments.Add(ConversionUtility.ConvertMessageToYamlComment(message));
                    }
                }
                if (gitHubActions?.jobs != null)
                {
                    //Add each individual step comments
                    foreach (KeyValuePair <string, GitHubActions.Job> job in gitHubActions.jobs)
                    {
                        if (job.Value.steps != null)
                        {
                            if (job.Value.job_message != null)
                            {
                                stepComments.Add(ConversionUtility.ConvertMessageToYamlComment(job.Value.job_message));
                            }
                            foreach (GitHubActions.Step step in job.Value.steps)
                            {
                                if (step != null && string.IsNullOrEmpty(step.step_message) == false)
                                {
                                    stepComments.Add(ConversionUtility.ConvertMessageToYamlComment(step.step_message));
                                }
                            }
                        }
                    }
                }
            }

            //Append all of the comments to the top of the file
            foreach (string item in stepComments)
            {
                gitHubYaml = item + System.Environment.NewLine + gitHubYaml;
            }

            //Return the final conversion result, with the original (pipeline) yaml, processed (actions) yaml, and any comments
            return(new ConversionResponse
            {
                pipelinesYaml = yaml,
                actionsYaml = gitHubYaml,
                comments = stepComments,
                v2ConversionSuccessful = true
            });
        }