private GitHubActions.Job ProcessIndividualJob(AzurePipelines.Job job, AzurePipelines.Resources resources) { GitHubActions.Job newJob = new GitHubActions.Job { name = job.displayName, needs = job.dependsOn, _if = ProcessCondition(job.condition), runs_on = ProcessPool(job.pool), strategy = ProcessStrategy(job.strategy), container = ProcessContainer(resources), env = ProcessSimpleVariables(job.variables), timeout_minutes = job.timeoutInMinutes, steps = ProcessSteps(job.steps) }; if (newJob.steps == null & job.template != null) { //Initialize the array with no items job.steps = new AzurePipelines.Step[0]; //Process the steps, adding the default checkout step newJob.steps = ProcessSteps(job.steps, true); //TODO: Find a way to allow GitHub jobs to reference another job as a template newJob.job_message += "Note: Azure DevOps template does not have an equivalent in GitHub Actions yet"; } else if (newJob.steps == null && job.strategy?.runOnce?.deploy?.steps != null) { //Initialize the array with no items job.steps = new AzurePipelines.Step[0]; //Process the steps, adding the default checkout step newJob.steps = ProcessSteps(job.strategy?.runOnce?.deploy?.steps, false); //TODO: Find a way to allow GitHub jobs to reference another job as a template newJob.job_message += "Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet"; } if (job.environment != null) { newJob.job_message += "Note: Azure DevOps job environment does not have an equivalent in GitHub Actions yet"; } if (newJob._if != null) { if (newJob.job_message != null) { newJob.job_message += Environment.NewLine; } } if (job.continueOnError) { newJob.continue_on_error = job.continueOnError; } return(newJob); }
public AzurePipelines.Job[] ProcessJobFromPipelineRootV2(string poolYaml, string strategyYaml, string stepsYaml) { Pool pool = null; if (poolYaml != null) { GeneralProcessing gp = new GeneralProcessing(_verbose); pool = gp.ProcessPoolV2(poolYaml); } AzurePipelines.Strategy strategy = null; try { //Most often, the pool will be in this structure strategy = GenericObjectSerialization.DeserializeYaml <AzurePipelines.Strategy>(strategyYaml); } catch (Exception ex) { ConversionUtility.WriteLine($"DeserializeYaml<AzurePipelines.Strategy>(strategyYaml) swallowed an exception: " + ex.Message, _verbose); } AzurePipelines.Step[] steps = null; if (stepsYaml != null) { try { steps = GenericObjectSerialization.DeserializeYaml <AzurePipelines.Step[]>(stepsYaml); } catch (Exception ex) { ConversionUtility.WriteLine($"DeserializeYaml<AzurePipelines.Step[]>(stepsYaml) swallowed an exception: " + ex.Message, _verbose); } } AzurePipelines.Job job = new AzurePipelines.Job { pool = pool, strategy = strategy, steps = steps }; //Don't add the build name unless there is content if (job.pool != null || job.strategy != null || steps != null) { AzurePipelines.Job[] jobs = new AzurePipelines.Job[1]; job.job = "build"; jobs[0] = job; return(jobs); } else { return(null); } }
/// <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 }); }
public GitHubActions.Job ProcessJob(AzurePipelines.Job job, AzurePipelines.Resources resources) { GeneralProcessing generalProcessing = new GeneralProcessing(_verbose); VariablesProcessing vp = new VariablesProcessing(_verbose); StepsProcessing sp = new StepsProcessing(); GitHubActions.Job newJob = new GitHubActions.Job { name = job.displayName, needs = job.dependsOn, _if = ConditionsProcessing.TranslateConditions(job.condition), runs_on = generalProcessing.ProcessPool(job.pool), strategy = generalProcessing.ProcessStrategy(job.strategy), container = generalProcessing.ProcessContainer(resources), env = vp.ProcessSimpleVariables(job.variables), timeout_minutes = job.timeoutInMinutes, steps = sp.AddSupportingSteps(job.steps) }; MatrixVariableName = generalProcessing.MatrixVariableName; VariableList = vp.VariableList; if (newJob.steps == null & job.template != null) { //Initialize the array with no items job.steps = new AzurePipelines.Step[0]; //Process the steps, adding the default checkout step newJob.steps = sp.AddSupportingSteps(job.steps, true); //TODO: There is currently no conversion path for templates newJob.job_message += "Note: Azure DevOps template does not have an equivalent in GitHub Actions yet"; } else if (newJob.steps == null && job.strategy?.runOnce?.deploy?.steps != null) { //Initialize the array with no items job.steps = new AzurePipelines.Step[0]; //Process the steps, adding the default checkout step newJob.steps = sp.AddSupportingSteps(job.strategy?.runOnce?.deploy?.steps, false); //TODO: There is currently no conversion path for templates newJob.job_message += "Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet"; } if (job.environment != null) { newJob.job_message += "Note: Azure DevOps job environment does not have an equivalent in GitHub Actions yet"; } if (job.continueOnError == true) { newJob.continue_on_error = job.continueOnError; } return(newJob); }
//Used by jobs and stages public static string GenerateJobName(AzurePipelines.Job job, int currentIndex) { //Get the job name string jobName = job.job; if (jobName == null && job.deployment != null) { jobName = job.deployment; } if (jobName == null && job.template != null) { jobName = "Template"; } if (string.IsNullOrEmpty(jobName) == true) { jobName = "job" + currentIndex.ToString(); } return(jobName); }
public AzurePipelines.Job[] ExtractAzurePipelinesJobsV2(JToken jobsJson, string strategyYaml) { GeneralProcessing gp = new GeneralProcessing(_verbose); AzurePipelines.Job[] jobs = new AzurePipelines.Job[jobsJson.Count()]; if (jobsJson != null) { int i = 0; foreach (JToken jobJson in jobsJson) { AzurePipelines.Job job = new AzurePipelines.Job { job = jobJson["job"]?.ToString(), deployment = jobJson["deployment"]?.ToString(), displayName = jobJson["displayName"]?.ToString(), template = jobJson["template"]?.ToString() }; if (jobJson["pool"] != null) { job.pool = gp.ProcessPoolV2(jobJson["pool"].ToString()); } if (jobJson["strategy"] != null) { job.strategy = gp.ProcessStrategyV2(jobJson["strategy"].ToString()); } else if (strategyYaml != null) { job.strategy = gp.ProcessStrategyV2(strategyYaml); } if (jobJson["dependsOn"] != null) { job.dependsOn = gp.ProcessDependsOnV2(jobJson["dependsOn"].ToString()); } if (jobJson["condition"] != null) { job.condition = ConditionsProcessing.TranslateConditions(jobJson["condition"].ToString()); } if (jobJson["environment"] != null) { job.environment = gp.ProcessEnvironmentV2(jobJson["environment"].ToString()); } if (jobJson["timeoutInMinutes"] != null) { int.TryParse(jobJson["timeoutInMinutes"].ToString(), out int timeOut); if (timeOut > 0) { job.timeoutInMinutes = timeOut; } } if (jobJson["continueOnError"] != null) { bool.TryParse(jobJson["continueOnError"].ToString(), out bool continueOnError); job.continueOnError = continueOnError; } if (jobJson["variables"] != null) { VariablesProcessing vp = new VariablesProcessing(_verbose); job.variables = vp.ProcessParametersAndVariablesV2(null, jobJson["variables"].ToString()); } if (jobJson["steps"] != null) { try { job.steps = GenericObjectSerialization.DeserializeYaml <AzurePipelines.Step[]>(jobJson["steps"].ToString()); } catch (Exception ex) { ConversionUtility.WriteLine($"DeserializeYaml<AzurePipelines.Step[]>(jobJson[\"steps\"].ToString() swallowed an exception: " + ex.Message, _verbose); } } jobs[i] = job; i++; } } return(jobs); }
public Dictionary <string, GitHubActions.Job> ProcessStagesV2(JToken stagesJson, string strategyYaml) { AzurePipelines.Job[] jobs = null; List <AzurePipelines.Stage> stages = new List <AzurePipelines.Stage>(); if (stagesJson != null) { //for each stage foreach (JToken stageJson in stagesJson) { AzurePipelines.Stage stage = new AzurePipelines.Stage { stage = stageJson["stage"]?.ToString(), displayName = stageJson["displayName"]?.ToString(), condition = stageJson["condition"]?.ToString() }; if (stageJson["dependsOn"] != null) { GeneralProcessing gp = new GeneralProcessing(_verbose); stage.dependsOn = gp.ProcessDependsOnV2(stageJson["dependsOn"].ToString()); } if (stageJson["variables"] != null) { VariablesProcessing vp = new VariablesProcessing(_verbose); stage.variables = vp.ProcessParametersAndVariablesV2(null, stageJson["variables"].ToString()); } if (stageJson["jobs"] != null) { JobProcessing jp = new JobProcessing(_verbose); stage.jobs = jp.ExtractAzurePipelinesJobsV2(stageJson["jobs"], strategyYaml); } stages.Add(stage); } //process the jobs if (stages != null) { int jobCount = 0; foreach (Stage stage in stages) { if (stage.jobs != null) { jobCount += stage.jobs.Length; } } jobs = new AzurePipelines.Job[jobCount]; //Giant nested loop ahead. Loop through stages, looking for all jobs int jobIndex = 0; foreach (Stage stage in stages) { if (stage.jobs != null) { for (int i = 0; i < stage.jobs.Length; i++) { jobs[jobIndex] = stage.jobs[i]; if (stage.variables != null) { if (jobs[jobIndex].variables == null) { jobs[jobIndex].variables = new Dictionary <string, string>(); } foreach (KeyValuePair <string, string> stageVariable in stage.variables) { //Add the stage variable if it doesn't already exist if (jobs[jobIndex].variables.ContainsKey(stageVariable.Key) == false) { jobs[jobIndex].variables.Add(stageVariable.Key, stageVariable.Value); } } } if (stage.condition != null) { jobs[jobIndex].condition = stage.condition; } //Get the job name string jobName = ConversionUtility.GenerateJobName(stage.jobs[i], jobIndex); //Rename the job, using the stage name as prefix, so that we keep the job names unique jobs[jobIndex].job = stage.stage + "_Stage_" + jobName; jobIndex++; } } } } } //Build the final list of GitHub jobs and return it if (jobs != null) { Dictionary <string, GitHubActions.Job> gitHubJobs = new Dictionary <string, GitHubActions.Job>(); foreach (AzurePipelines.Job job in jobs) { JobProcessing jobProcessing = new JobProcessing(_verbose); gitHubJobs.Add(job.job, jobProcessing.ProcessJob(job, null)); } return(gitHubJobs); } else { return(null); } }