From 4e85695748d7c9e946d8ef3fa3591d50842b3c0d Mon Sep 17 00:00:00 2001 From: Sam Smith Date: Mon, 21 Dec 2020 08:11:58 -0500 Subject: [PATCH 1/8] Added support for Hugo task --- .../StepsProcessing.cs | 41 +++++++++++++++++++ .../StepsTests.cs | 30 ++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StepsProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StepsProcessing.cs index 78860eda..705268d2 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StepsProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StepsProcessing.cs @@ -80,6 +80,9 @@ public GitHubActions.Step ProcessStep(AzurePipelines.Step step) case "GRADLE@2": gitHubStep = CreateGradleStep(step); break; + case "HUGOTASK@1": + gitHubStep = CreateHugoStep(step); + break; //case "KUBERNETES@1": // gitHubStep = CreateKubernetesStep(step); // break; @@ -1286,6 +1289,44 @@ public GitHubActions.Step CreateGradleStep(AzurePipelines.Step step) return gitHubStep; } + public GitHubActions.Step CreateHugoStep(AzurePipelines.Step step) + { + //coming from: + //- task: HugoTask@1 + // displayName: 'Generate Hugo site' + // inputs: + // hugoVersion: latest + // extendedVersion: true + // destination: '$(Build.ArtifactStagingDirectory)' + + //Going to: https://github.com/peaceiris/actions-hugo + //- name: Generate Hugo site + // uses: peaceiris/actions-hugo@v2 + // with: + // hugo-version: latest + // extended: true + + string hugoVersion = GetStepInput(step, "hugoVersion"); + string extendedVersion = GetStepInput(step, "extendedVersion"); + //string destination = GetStepInput(step, "destination"); + + GitHubActions.Step gitHubStep = new GitHubActions.Step + { + uses = "peaceiris/actions-hugo@v2", + with = new Dictionary + { + { "hugo-version", hugoVersion} + }, + step_message = "Note: This is a third party action: https://github.com/peaceiris/actions-hugo" + }; + if (extendedVersion != null) + { + gitHubStep.with.Add("extended", extendedVersion); + } + + return gitHubStep; + } + //TODO: Finish this Kubernetes Step //public GitHubActions.Step CreateKubernetesStep(AzurePipelines.Step step) //{ diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs index 77aa10d8..ccf1f2f5 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs @@ -1024,6 +1024,36 @@ Changes in this Release Assert.AreEqual(expected, gitHubOutput.actionsYaml); } + [TestMethod] + public void HugoIndividualStepTest() + { + //Arrange + Conversion conversion = new Conversion(); + string yaml = @" +- task: HugoTask@1 + displayName: 'Generate Hugo site' + inputs: + hugoVersion: latest + extendedVersion: true + destination: '$(Build.ArtifactStagingDirectory)' +"; + + //Act + ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineTaskToGitHubActionTask(yaml); + + //Assert + string expected = @" +- # 'Note: This is a third party action: https://github.com/peaceiris/actions-hugo' + name: Generate Hugo site + uses: peaceiris/actions-hugo@v2 + with: + hugo-version: latest + extended: true +"; + expected = UtilityTests.TrimNewLines(expected); + Assert.AreEqual(expected, gitHubOutput.actionsYaml); + } + // [TestMethod] // public void IISWebManagementStepTest() // { From 1a922436fb8c84472d05c161e8611ba794a26a91 Mon Sep 17 00:00:00 2001 From: Sam Smith Date: Mon, 21 Dec 2020 09:12:34 -0500 Subject: [PATCH 2/8] Fix to conditions that caused it to be processed twice and hence return an incomplete condition --- .../ConditionsProcessing.cs | 2 +- .../JobProcessing.cs | 2 +- .../CompletePipelineTests.cs | 4 +- .../ConditionTests.cs | 14 +-- .../ExampleTests.cs | 113 ++++++++++++++++++ .../StagesTests.cs | 2 +- .../StepsTests.cs | 2 +- 7 files changed, 126 insertions(+), 13 deletions(-) create mode 100644 src/AzurePipelinesToGitHubActionsConverter.Tests/ExampleTests.cs diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/ConditionsProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/ConditionsProcessing.cs index c24973f6..71d46cea 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/ConditionsProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/ConditionsProcessing.cs @@ -39,7 +39,7 @@ public static string TranslateConditions(string condition) innerContentsProcessed += TranslateConditions(innerContent); if (i != innerContents.Count - 1) { - innerContentsProcessed += ","; + innerContentsProcessed += ", "; } } contents = innerContentsProcessed; diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs index 3b552622..97aaf397 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs @@ -130,7 +130,7 @@ public AzurePipelines.Job[] ExtractAzurePipelinesJobsV2(JToken jobsJson, string } if (jobJson["condition"] != null) { - job.condition = ConditionsProcessing.TranslateConditions(jobJson["condition"].ToString()); + job.condition = jobJson["condition"].ToString(); } if (jobJson["environment"] != null) { diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/CompletePipelineTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/CompletePipelineTests.cs index 25f10919..bf8ed796 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/CompletePipelineTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/CompletePipelineTests.cs @@ -1313,7 +1313,7 @@ public void SSParentPipelineTest() prId: 000 prUC: PR${{ env.prId }} prLC: pr${{ env.prId }} - if: and(success(),eq(variables['Build.Reason'], 'PullRequest'),ne(variables['System.PullRequest.PullRequestId'], 'Null')) + if: and(success(), eq(variables['Build.Reason'], 'PullRequest'), ne(variables['System.PullRequest.PullRequestId'], 'Null')) steps: - uses: actions/checkout@v2 "; @@ -2151,7 +2151,7 @@ public void JLPipelineTest() Deploy_Stage_Deploy_Module: name: Deploy Module runs-on: ubuntu 16.04 - if: and(success(),or(eq(github.ref, 'refs/heads/master'),startsWith(github.ref, 'refs/tags/')),contains(variables['System.TeamFoundationCollectionUri'], 'dsccommunity')) + if: and(success(), or(eq(github.ref, 'refs/heads/master'), startsWith(github.ref, 'refs/tags/')), contains(variables['System.TeamFoundationCollectionUri'], 'dsccommunity')) steps: - uses: actions/checkout@v2 - name: Download Build Artifact diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/ConditionTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/ConditionTests.cs index 52027702..6e34c180 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/ConditionTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/ConditionTests.cs @@ -115,7 +115,7 @@ public void AndEqualsNotEqualsTest() string result = ConditionsProcessing.TranslateConditions(condition); //Assert - string expected = "and(eq('ABCDE', 'BCD'),ne(0, 1))"; + string expected = "and(eq('ABCDE', 'BCD'), ne(0, 1))"; Assert.AreEqual(expected, result); } @@ -129,7 +129,7 @@ public void BranchVariableTest() string result = ConditionsProcessing.TranslateConditions(condition); //Assert - string expected = "and(success(),eq(github.ref, 'refs/heads/master'))"; + string expected = "and(success(), eq(github.ref, 'refs/heads/master'))"; Assert.AreEqual(expected, result); } @@ -143,7 +143,7 @@ public void BranchNameVariableTest() string result = ConditionsProcessing.TranslateConditions(condition); //Assert - string expected = "and(success(),endsWith(github.ref, 'master'))"; + string expected = "and(success(), endsWith(github.ref, 'master'))"; Assert.AreEqual(expected, result); } @@ -165,7 +165,7 @@ public void MultilineConditionTest() string result = ConditionsProcessing.TranslateConditions(text); //Assert - string expected = "and(success(),or(eq(github.ref, 'refs/heads/master'),startsWith(github.ref, 'refs/tags/')),contains(variables['System.TeamFoundationCollectionUri'], 'dsccommunity'))"; + string expected = "and(success(), or(eq(github.ref, 'refs/heads/master'), startsWith(github.ref, 'refs/tags/')), contains(variables['System.TeamFoundationCollectionUri'], 'dsccommunity'))"; Assert.AreEqual(expected, result); } @@ -180,7 +180,7 @@ public void AndOrConditionTest() string result = ConditionsProcessing.TranslateConditions(text); //Assert - string expected = "and(success(),or(success(),success()))"; + string expected = "and(success(), or(success(), success()))"; Assert.AreEqual(expected, result); } @@ -195,7 +195,7 @@ public void AndUpperCaseOrConditionTest() string result = ConditionsProcessing.TranslateConditions(text); //Assert - string expected = "and(success(),OR(success(),success()))"; + string expected = "and(success(), OR(success(), success()))"; Assert.AreEqual(expected, result); } @@ -227,7 +227,7 @@ public void AndEqualAndNotEqualComplexTest() string result = ConditionsProcessing.TranslateConditions(text); //Assert - string expected = "and(success(),eq(variables['Build.Reason'], 'PullRequest'),ne(variables['System.PullRequest.PullRequestId'], 'Null'))"; + string expected = "and(success(), eq(variables['Build.Reason'], 'PullRequest'), ne(variables['System.PullRequest.PullRequestId'], 'Null'))"; Assert.AreEqual(expected, result); } diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/ExampleTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/ExampleTests.cs new file mode 100644 index 00000000..44913cd2 --- /dev/null +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/ExampleTests.cs @@ -0,0 +1,113 @@ +using AzurePipelinesToGitHubActionsConverter.Core; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace AzurePipelinesToGitHubActionsConverter.Tests +{ + [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [TestClass] + public class ExampleTests + { + [TestMethod] + public void CDTest() + { + //Arrange + string input = @" +trigger: +- master + +variables: + buildConfiguration: 'Release' + buildPlatform: 'Any CPU' + +jobs: +- job: Deploy + displayName: ""Deploy job"" + pool: + vmImage: ubuntu-latest + condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) + variables: + AppSettings.Environment: 'data' + ArmTemplateResourceGroupLocation: 'eu' + ResourceGroupName: 'MyProjectRG' + WebsiteName: 'myproject-web' + steps: + - task: DownloadBuildArtifacts@0 + displayName: 'Download the build artifacts' + inputs: + buildType: 'current' + downloadType: 'single' + artifactName: 'drop' + downloadPath: '$(build.artifactstagingdirectory)' + - task: AzureRmWebAppDeployment@3 + displayName: 'Azure App Service Deploy: web site' + inputs: + azureSubscription: 'connection to Azure Portal' + WebAppName: $(WebsiteName) + DeployToSlotFlag: true + ResourceGroupName: $(ResourceGroupName) + SlotName: 'staging' + Package: '$(build.artifactstagingdirectory)/drop/MyProject.Web.zip' + TakeAppOfflineFlag: true + JSONFiles: '**/appsettings.json' + - task: AzureAppServiceManage@0 + displayName: 'Swap Slots: website' + inputs: + azureSubscription: 'connection to Azure Portal' + WebAppName: $(WebsiteName) + ResourceGroupName: $(ResourceGroupName) + SourceSlot: 'staging'"; + Conversion conversion = new Conversion(); + + //Act + ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineToGitHubAction(input); + + //Assert + string expected = @" +#Note: 'AZURE_SP' secret is required to be setup and added into GitHub Secrets: https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets +on: + push: + branches: + - master +env: + buildConfiguration: Release + buildPlatform: Any CPU +jobs: + Deploy: + name: Deploy job + runs-on: ubuntu-latest + env: + AppSettings.Environment: data + ArmTemplateResourceGroupLocation: eu + ResourceGroupName: MyProjectRG + WebsiteName: myproject-web + if: and(success(), eq(github.ref, 'refs/heads/master')) + steps: + - uses: actions/checkout@v2 + - # ""Note: 'AZURE_SP' secret is required to be setup and added into GitHub Secrets: https://help.github.com/en/actions/automating-your-workflow-with-github-actions/creating-and-using-encrypted-secrets"" + name: Azure Login + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_SP }} + - name: Download the build artifacts + uses: actions/download-artifact@v2 + with: + name: drop + path: ${{ github.workspace }} + - name: 'Azure App Service Deploy: web site' + uses: Azure/webapps-deploy@v2 + with: + app-name: ${{ env.WebsiteName }} + package: ${{ github.workspace }}/drop/MyProject.Web.zip + slot-name: staging + - name: 'Swap Slots: website' + uses: Azure/cli@v1.0.0 + with: + inlineScript: az webapp deployment slot swap --resource-group ${{ env.ResourceGroupName }} --name ${{ env.WebsiteName }} --slot staging --target-slot production +"; + + expected = UtilityTests.TrimNewLines(expected); + Assert.AreEqual(expected, gitHubOutput.actionsYaml); + } + } +} diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/StagesTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/StagesTests.cs index dd028105..47f2d6cd 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/StagesTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/StagesTests.cs @@ -129,7 +129,7 @@ public void LargeMultiStagePipelineTest() ResourceGroupName: MyProjectFeatureFlags WebsiteName: featureflags-data-eu-web WebServiceName: featureflags-data-eu-service - if: and(success(),eq(github.ref, 'refs/heads/master')) + if: and(success(), eq(github.ref, 'refs/heads/master')) continue-on-error: true steps: - uses: actions/checkout@v2 diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs index ccf1f2f5..7ce45f55 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs @@ -416,7 +416,7 @@ public void PowershellWithConditionIndividualStepTest() - name: PowerShell test task run: Write-Host 'Hello World' shell: powershell - if: and(eq('ABCDE', 'BCD'),ne(0, 1)) + if: and(eq('ABCDE', 'BCD'), ne(0, 1)) "; expected = UtilityTests.TrimNewLines(expected); Assert.AreEqual(expected, gitHubOutput.actionsYaml); From 33ee0ab94981d74a363b9f1b29e3d440d1f4b0a0 Mon Sep 17 00:00:00 2001 From: Sam Smith Date: Mon, 21 Dec 2020 10:10:02 -0500 Subject: [PATCH 3/8] Fixed an exception caused when parsing canary and rolling strategies --- .../AzurePipelinesModel/Canary.cs | 14 ++ .../AzurePipelinesModel/Deploy.cs | 2 +- .../AzurePipelinesModel/On.cs | 10 + .../AzurePipelinesModel/Rolling.cs | 14 ++ .../AzurePipelinesModel/RunOnce.cs | 6 +- .../AzurePipelinesModel/Step.cs | 2 + .../AzurePipelinesModel/Strategy.cs | 2 + .../GeneralProcessing.cs | 46 +++-- .../JobProcessing.cs | 20 +- .../StepsProcessing.cs | 38 ++++ .../StepsTests.cs | 26 +++ .../StrategyTests.cs | 175 +++++++++++++++++- 12 files changed, 324 insertions(+), 31 deletions(-) create mode 100644 src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Canary.cs create mode 100644 src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/On.cs create mode 100644 src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Rolling.cs diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Canary.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Canary.cs new file mode 100644 index 00000000..56f621db --- /dev/null +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Canary.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace AzurePipelinesToGitHubActionsConverter.Core.AzurePipelines +{ + public class Canary + { + public int[] increments { get; set; } + public Deploy preDeploy { get; set; } + public Deploy deploy { get; set; } + public Deploy routeTraffic { get; set; } + public Deploy postRouteTraffic { get; set; } + public On on { get; set; } + } +} diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Deploy.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Deploy.cs index 6236d6ea..88ba2046 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Deploy.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Deploy.cs @@ -4,7 +4,7 @@ namespace AzurePipelinesToGitHubActionsConverter.Core.AzurePipelines { public class Deploy { + public Pool pool { get; set; } public Step[] steps { get; set; } - } } diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/On.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/On.cs new file mode 100644 index 00000000..ab694331 --- /dev/null +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/On.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace AzurePipelinesToGitHubActionsConverter.Core.AzurePipelines +{ + public class On + { + public Deploy failure { get; set; } + public Deploy success { get; set; } + } +} diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Rolling.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Rolling.cs new file mode 100644 index 00000000..a986cd7f --- /dev/null +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Rolling.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; + +namespace AzurePipelinesToGitHubActionsConverter.Core.AzurePipelines +{ + public class Rolling + { + public int maxParallel { get; set; } + public Deploy preDeploy { get; set; } + public Deploy deploy { get; set; } + public Deploy routeTraffic { get; set; } + public Deploy postRouteTraffic { get; set; } + public On on { get; set; } + } +} diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/RunOnce.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/RunOnce.cs index 9781f1b5..15940405 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/RunOnce.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/RunOnce.cs @@ -3,7 +3,11 @@ namespace AzurePipelinesToGitHubActionsConverter.Core.AzurePipelines { public class RunOnce - { + { + public Deploy preDeploy { get; set; } public Deploy deploy { get; set; } + public Deploy routeTraffic { get; set; } + public Deploy postRouteTraffic { get; set; } + public On on { get; set; } } } diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Step.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Step.cs index 0b2110ee..76ca8538 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Step.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Step.cs @@ -83,7 +83,9 @@ public string powershell public string task { get; set; } public string template { get; set; } public string publish { get; set; } + public string download { get; set; } public string artifact { get; set; } + public string patterns { get; set; } public string displayName { get; set; } public string name { get; set; } public string condition { get; set; } diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Strategy.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Strategy.cs index 172335ef..e386ad29 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Strategy.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/AzurePipelinesModel/Strategy.cs @@ -33,5 +33,7 @@ public class Strategy public string maxParallel { get; set; } public RunOnce runOnce { get; set; } + public Canary canary { get; set; } + public Rolling rolling { get; set; } } } diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs index 218e1011..bfecee32 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs @@ -19,14 +19,7 @@ public GeneralProcessing(bool verbose) public string ProcessNameV2(string nameYaml) { - if (nameYaml != null) - { - return nameYaml.Replace("name:", "").Replace(System.Environment.NewLine, "").Trim(); - } - else - { - return null; - } + return nameYaml.Replace("name:", "").Replace(System.Environment.NewLine, "").Trim(); } public string[] ProcessDependsOnV2(string dependsOnYaml) @@ -79,8 +72,10 @@ public AzurePipelines.Environment ProcessEnvironmentV2(string environmentYaml) JObject json = JsonSerialization.DeserializeStringToObject(environmentYaml); if (json["name"] != null) { - environment = new AzurePipelines.Environment(); - environment.name = json["name"].ToString(); + environment = new AzurePipelines.Environment + { + name = json["name"].ToString() + }; } if (json["resourceName"] != null) { @@ -135,16 +130,17 @@ public AzurePipelines.Strategy ProcessStrategyV2(string strategyYaml) { if (strategyYaml != null) { - //try - //{ - //Most often, the pool will be in this structure - AzurePipelines.Strategy strategy = YamlSerialization.DeserializeYaml(strategyYaml); - return strategy; - //} - //catch (Exception ex) - //{ - // ConversionUtility.WriteLine($"DeserializeYaml(strategyYaml) swallowed an exception: " + ex.Message, _verbose); - //} + try + { + //Most often, the pool will be in this structure + AzurePipelines.Strategy strategy = YamlSerialization.DeserializeYaml(strategyYaml); + return strategy; + } + catch (Exception ex) + { + ConversionUtility.WriteLine($"DeserializeYaml(strategyYaml) swallowed an exception: " + ex.Message, _verbose); + return null; + } } else { @@ -296,6 +292,16 @@ public GitHubActions.Strategy ProcessStrategy(AzurePipelines.Strategy strategy) //TODO: There is currently no conversion path for other strategies ConversionUtility.WriteLine("TODO: " + strategy.runOnce, _verbose); } + if (strategy.canary != null) + { + //TODO: There is currently no conversion path for other strategies + ConversionUtility.WriteLine("TODO: " + strategy.canary, _verbose); + } + if (strategy.rolling != null) + { + //TODO: There is currently no conversion path for other strategies + ConversionUtility.WriteLine("TODO: " + strategy.rolling, _verbose); + } return processedStrategy; } else diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs index 97aaf397..200f6348 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs @@ -50,13 +50,23 @@ public GitHubActions.Job ProcessJob(AzurePipelines.Job job, AzurePipelines.Resou } 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 + if (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 runOnce strategy newJob.job_message += "Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet"; } + if (newJob.runs_on == null && job.strategy?.runOnce?.deploy?.pool != null) + { + if (job.strategy?.runOnce?.deploy?.pool != null) + { + newJob.runs_on = generalProcessing.ProcessPool(job.strategy?.runOnce?.deploy?.pool); + } + } if (job.continueOnError == true) { newJob.continue_on_error = job.continueOnError; diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StepsProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StepsProcessing.cs index 705268d2..1e09645e 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StepsProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StepsProcessing.cs @@ -193,6 +193,10 @@ public GitHubActions.Step ProcessStep(AzurePipelines.Step step) { gitHubStep = CreateTemplateStep(step); } + else if (step.download != null) + { + gitHubStep = CreateDownloadStep(step); + } if (gitHubStep != null) { @@ -297,6 +301,40 @@ private GitHubActions.Step CreateDotNetCommandStep(AzurePipelines.Step step) } } + private GitHubActions.Step CreateDownloadStep(AzurePipelines.Step step) + { + //From: + //- download: [ current | pipeline resource identifier | none ] # disable automatic download if "none" + // artifact: string ## artifact name, optional; downloads all the available artifacts if not specified + // patterns: string # patterns representing files to include; optional + // displayName: string # friendly name to display in the UI + + //To: + //- name: Download serviceapp artifact + // uses: actions/download-artifact@v1.0.0 + // with: + // name: serviceapp + + string artifact = step.artifact; + string patterns = step.patterns; + + GitHubActions.Step gitHubStep = new GitHubActions.Step + { + uses = "actions/download-artifact@v2", + with = new Dictionary() + }; + if (artifact != null) + { + gitHubStep.with.Add("name", artifact); + } + else if (patterns != null) + { + gitHubStep.with.Add("name", patterns); + } + + return gitHubStep; + } + private GitHubActions.Step CreateDownloadBuildArtifactsStep(AzurePipelines.Step step) { //From: diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs index 7ce45f55..99ff762d 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/StepsTests.cs @@ -637,6 +637,32 @@ public void DeployWebAppIndividualStepTest() Assert.AreEqual(expected, gitHubOutput.actionsYaml); } + [TestMethod] + public void DownloadIndividualStepTest() + { + //Arrange + Conversion conversion = new Conversion(); + string yaml = @" +- download: current # refers to artifacts published by current pipeline + artifact: WebApp + patterns: '**/.js' + displayName: Download artifact WebApp +"; + + //Act + ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineTaskToGitHubActionTask(yaml); + + //Assert + string expected = @" +- name: Download artifact WebApp + uses: actions/download-artifact@v2 + with: + name: WebApp +"; + expected = UtilityTests.TrimNewLines(expected); + Assert.AreEqual(expected, gitHubOutput.actionsYaml); + } + [TestMethod] public void DownloadBuildArtifactsIndividualStepTest() { diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs index 933ac034..bac8e3bd 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs @@ -1,4 +1,4 @@ -using AzurePipelinesToGitHubActionsConverter.Core; +using AzurePipelinesToGitHubActionsConverter.Core; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace AzurePipelinesToGitHubActionsConverter.Tests @@ -78,13 +78,74 @@ public void StrategyTest() expected = UtilityTests.TrimNewLines(expected); Assert.AreEqual(expected, gitHubOutput.actionsYaml); - + + } + + [TestMethod] + public void StrategyWithMaxParallelOnlyTest() + { + //strategy: + // matrix: + // linux: + // imageName: "ubuntu-16.04" + // mac: + // imageName: "macos-10.13" + // windows: + // imageName: "vs2017-win2016" + + //Arrange + Conversion conversion = new Conversion(); + string yaml = @" +trigger: +- master +strategy: + maxParallel: 3 +jobs: +- job: Build + displayName: Build job + pool: + vmImage: $(imageName) + variables: + buildConfiguration: Debug + steps: + - script: dotnet build WebApplication1/WebApplication1.Service/WebApplication1.Service.csproj --configuration $(buildConfiguration) + displayName: dotnet build part 1 +"; + + //Act + ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineToGitHubAction(yaml); + + //Assert + //Note that we are using the longer form, as sequence flow (showing an array like: [ubuntu-16.04, macos-10.13, vs2017-win2016]), doesn't exist in this YAML Serializer yet. + string expected = @" +on: + push: + branches: + - master +jobs: + Build: + name: Build job + runs-on: ${{ env.imageName }} + strategy: + max-parallel: 3 + env: + buildConfiguration: Debug + steps: + - uses: actions/checkout@v2 + - name: dotnet build part 1 + run: dotnet build WebApplication1/WebApplication1.Service/WebApplication1.Service.csproj --configuration ${{ env.buildConfiguration }} +"; + + expected = UtilityTests.TrimNewLines(expected); + Assert.AreEqual(expected, gitHubOutput.actionsYaml); + } [TestMethod] public void StrategyRunOnceDeploymentTest() { //Arrange + Conversion conversion = new Conversion(); string input = @" jobs: - deployment: DeployInfrastructure @@ -102,7 +163,6 @@ public void StrategyRunOnceDeploymentTest() targetType: inline script: | Write-Host ""Hello world"""; - Conversion conversion = new Conversion(); //Act ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineToGitHubAction(input); @@ -130,6 +190,7 @@ public void StrategyRunOnceDeploymentTest() public void StrategyRunOnceWithComplexEnvironmentsDeploymentTest() { //Arrange + Conversion conversion = new Conversion(); string input = @" jobs: - deployment: DeployInfrastructure @@ -152,7 +213,6 @@ public void StrategyRunOnceWithComplexEnvironmentsDeploymentTest() targetType: inline script: | Write-Host ""Hello world"""; - Conversion conversion = new Conversion(); //Act ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineToGitHubAction(input); @@ -175,5 +235,112 @@ public void StrategyRunOnceWithComplexEnvironmentsDeploymentTest() Assert.AreEqual(expected, gitHubOutput.actionsYaml); } + + [TestMethod] + public void StrategyRollingWithComplexEnvironmentsDeploymentTest() + { + //Arrange + Conversion conversion = new Conversion(); + string input = @" +jobs: +- deployment: VMDeploy + displayName: web + environment: + name: smarthotel-dev + resourceType: VirtualMachine + strategy: + rolling: + maxParallel: 5 #for percentages, mention as x% + preDeploy: + steps: + - download: current + artifact: drop + - script: echo initialize, cleanup, backup, install certs + deploy: + steps: + - task: IISWebAppDeploymentOnMachineGroup@0 + displayName: 'Deploy application to Website' + inputs: + WebSiteName: 'Default Web Site' + Package: '$(Pipeline.Workspace)/drop/**/*.zip' + routeTraffic: + steps: + - script: echo routing traffic + postRouteTraffic: + steps: + - script: echo health check post-route traffic + on: + failure: + steps: + - script: echo Restore from backup! This is on failure + success: + steps: + - script: echo Notify! This is on success"; + + //Act + ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineToGitHubAction(input); + + //Assert + //TODO: Process the rest of the steps + string expected = @" +jobs: + VMDeploy: + name: web + environment: + name: smarthotel-dev +"; + expected = UtilityTests.TrimNewLines(expected); + Assert.AreEqual(expected, gitHubOutput.actionsYaml); + + } + + [TestMethod] + public void StrategyCanaryWithComplexEnvironmentsDeploymentTest() + { + //Arrange + Conversion conversion = new Conversion(); + string input = @" +jobs: +- deployment: VMDeploy + environment: smarthotel-dev.bookings + pool: + name: smarthotel-devPool + strategy: + canary: + increments: [10,20] + preDeploy: + steps: + - script: initialize, cleanup.... + deploy: + steps: + - script: echo deploy updates... + postRouteTraffic: + pool: server + steps: + - script: echo monitor application health... + on: + failure: + steps: + - script: echo clean-up, rollback... + success: + steps: + - script: echo checks passed, notify... "; + + //Act + ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineToGitHubAction(input); + + //Assert + //TODO: Process the rest of the steps + string expected = @" +jobs: + VMDeploy: + runs-on: smarthotel-devPool + environment: + name: smarthotel-dev.bookings +"; + expected = UtilityTests.TrimNewLines(expected); + Assert.AreEqual(expected, gitHubOutput.actionsYaml); + + } } } \ No newline at end of file From 368597b8c506d1469b8925199b636d355d048a3b Mon Sep 17 00:00:00 2001 From: Sam Smith Date: Mon, 21 Dec 2020 17:31:48 -0500 Subject: [PATCH 4/8] Updated to cover runonce, canary, and rollover strategies, at a very high level. (at least it doesn't crash anymore!) --- .../Conversion.cs | 9 +- .../ConditionsProcessing.cs | 2 +- .../GeneralProcessing.cs | 23 --- .../JobProcessing.cs | 59 ++++-- .../StagesProcessing.cs | 4 +- .../StrategyProcessing.cs | 184 ++++++++++++++++++ .../CompletePipelineTests.cs | 16 +- .../ConditionTests.cs | 28 +++ .../StrategyTests.cs | 75 +++++-- 9 files changed, 332 insertions(+), 68 deletions(-) create mode 100644 src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/Conversion.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/Conversion.cs index 4f4f1151..cd25ac7d 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/Conversion.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/Conversion.cs @@ -125,21 +125,18 @@ private ConversionResponse ConvertAzurePipelineToGitHubActionV2(string yaml) { 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); + gitHubActions.jobs = sp.ProcessStagesV2(json["stages"], json["strategy"]); } //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)); + gitHubActions.jobs = jp.ProcessJobsV2(jp.ExtractAzurePipelinesJobsV2(json["jobs"], json["strategy"]), 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 @@ -156,7 +153,7 @@ private ConversionResponse ConvertAzurePipelineToGitHubActionV2(string yaml) //Steps string stepsYaml = json["steps"]?.ToString(); JobProcessing jp = new JobProcessing(_verbose); - AzurePipelines.Job[] pipelineJobs = jp.ProcessJobFromPipelineRootV2(poolYaml, strategyYaml, stepsYaml); + AzurePipelines.Job[] pipelineJobs = jp.ProcessJobFromPipelineRootV2(poolYaml, json["strategy"], stepsYaml); Resources resources = gp.ExtractResourcesV2(resourcesYaml); gitHubActions.jobs = jp.ProcessJobsV2(pipelineJobs, resources); _matrixVariableName = jp.MatrixVariableName; diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/ConditionsProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/ConditionsProcessing.cs index 71d46cea..7dd70ea9 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/ConditionsProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/ConditionsProcessing.cs @@ -9,7 +9,7 @@ public static class ConditionsProcessing public static string TranslateConditions(string condition) { - if (condition == null) + if (string.IsNullOrEmpty(condition) == true) { return null; } diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs index bfecee32..7db0bf32 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs @@ -126,29 +126,6 @@ public Resources ExtractResourcesV2(string resourcesYaml) return null; } - public AzurePipelines.Strategy ProcessStrategyV2(string strategyYaml) - { - if (strategyYaml != null) - { - try - { - //Most often, the pool will be in this structure - AzurePipelines.Strategy strategy = YamlSerialization.DeserializeYaml(strategyYaml); - return strategy; - } - catch (Exception ex) - { - ConversionUtility.WriteLine($"DeserializeYaml(strategyYaml) swallowed an exception: " + ex.Message, _verbose); - return null; - } - } - else - { - return null; - } - } - - //process the build pool/agent public string ProcessPool(Pool pool) { diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs index 200f6348..c4f8b86b 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs @@ -3,7 +3,6 @@ using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; namespace AzurePipelinesToGitHubActionsConverter.Core.PipelinesToActionsConversion @@ -58,8 +57,34 @@ public GitHubActions.Job ProcessJob(AzurePipelines.Job job, AzurePipelines.Resou newJob.steps = sp.AddSupportingSteps(job.strategy?.runOnce?.deploy?.steps, false); } //TODO: There is currently no conversion path for runOnce strategy - newJob.job_message += "Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet"; + newJob.job_message += "Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps"; } + else if (newJob.steps == null && job.strategy?.canary?.deploy?.steps != null) + { + if (job.strategy?.canary?.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?.canary?.deploy?.steps, false); + } + //TODO: There is currently no conversion path for runOnce strategy + newJob.job_message += "Note: Azure DevOps strategy>canary does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps"; + } + else if (newJob.steps == null && job.strategy?.rolling?.deploy?.steps != null) + { + if (job.strategy?.rolling?.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?.rolling?.deploy?.steps, false); + } + //TODO: There is currently no conversion path for runOnce strategy + newJob.job_message += "Note: Azure DevOps strategy>rolling does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps"; + } + + if (newJob.runs_on == null && job.strategy?.runOnce?.deploy?.pool != null) { if (job.strategy?.runOnce?.deploy?.pool != null) @@ -106,9 +131,10 @@ public GitHubActions.Job ProcessJob(AzurePipelines.Job job, AzurePipelines.Resou } } - public AzurePipelines.Job[] ExtractAzurePipelinesJobsV2(JToken jobsJson, string strategyYaml) + public AzurePipelines.Job[] ExtractAzurePipelinesJobsV2(JToken jobsJson, JToken strategyJson) { GeneralProcessing gp = new GeneralProcessing(_verbose); + StrategyProcessing sp = new StrategyProcessing(_verbose); AzurePipelines.Job[] jobs = new AzurePipelines.Job[jobsJson.Count()]; if (jobsJson != null) { @@ -126,14 +152,16 @@ public AzurePipelines.Job[] ExtractAzurePipelinesJobsV2(JToken jobsJson, string { job.pool = gp.ProcessPoolV2(jobJson["pool"].ToString()); } + //Strategy if (jobJson["strategy"] != null) { - job.strategy = gp.ProcessStrategyV2(jobJson["strategy"].ToString()); + strategyJson = jobJson["strategy"]; } - else if (strategyYaml != null) + if (strategyJson != null) { - job.strategy = gp.ProcessStrategyV2(strategyYaml); + job.strategy = sp.ProcessStrategyV2(strategyJson); } + if (jobJson["dependsOn"] != null) { job.dependsOn = gp.ProcessDependsOnV2(jobJson["dependsOn"].ToString()); @@ -203,24 +231,21 @@ public AzurePipelines.Job[] ExtractAzurePipelinesJobsV2(JToken jobsJson, string return jobs; } - public AzurePipelines.Job[] ProcessJobFromPipelineRootV2(string poolYaml, string strategyYaml, string stepsYaml) + public AzurePipelines.Job[] ProcessJobFromPipelineRootV2(string poolYaml, JToken strategyJson, string stepsYaml) { + //Pool 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 = YamlSerialization.DeserializeYaml(strategyYaml); - } - catch (Exception ex) - { - ConversionUtility.WriteLine($"DeserializeYaml(strategyYaml) swallowed an exception: " + ex.Message, _verbose); - } + + //Strategy + StrategyProcessing sp = new StrategyProcessing(_verbose); + AzurePipelines.Strategy strategy = sp.ProcessStrategyV2(strategyJson); + + //Steps AzurePipelines.Step[] steps = null; if (stepsYaml != null) { diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StagesProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StagesProcessing.cs index 0e8f08e6..104308e1 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StagesProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StagesProcessing.cs @@ -12,7 +12,7 @@ public StagesProcessing(bool verbose) _verbose = verbose; } - public Dictionary ProcessStagesV2(JToken stagesJson, string strategyYaml) + public Dictionary ProcessStagesV2(JToken stagesJson, JToken strategyJson) { AzurePipelines.Job[] jobs = null; List stages = new List(); @@ -40,7 +40,7 @@ public StagesProcessing(bool verbose) if (stageJson["jobs"] != null) { JobProcessing jp = new JobProcessing(_verbose); - stage.jobs = jp.ExtractAzurePipelinesJobsV2(stageJson["jobs"], strategyYaml); + stage.jobs = jp.ExtractAzurePipelinesJobsV2(stageJson["jobs"], strategyJson); } if (stageJson["pool"] != null && stage.jobs != null) { diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs new file mode 100644 index 00000000..ff679a4d --- /dev/null +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs @@ -0,0 +1,184 @@ +using AzurePipelinesToGitHubActionsConverter.Core.AzurePipelines; +using AzurePipelinesToGitHubActionsConverter.Core.Serialization; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; + +namespace AzurePipelinesToGitHubActionsConverter.Core.PipelinesToActionsConversion +{ + public class StrategyProcessing + { + private readonly bool _verbose; + public StrategyProcessing(bool verbose) + { + _verbose = verbose; + } + + public Strategy ProcessStrategyV2(JToken strategyJson) + { + if (strategyJson != null) + { + AzurePipelines.Strategy newStrategy = new AzurePipelines.Strategy(); + if (strategyJson["parallel"] != null) + { + newStrategy.parallel = strategyJson["parallel"].ToString(); + ConversionUtility.WriteLine("Note that Parallel doesn't currently have an equivalent in Actions: " + newStrategy.parallel, _verbose); + } + if (strategyJson["matrix"] != null) + { + newStrategy.matrix = YamlSerialization.DeserializeYaml>>(strategyJson["matrix"].ToString()); + } + if (strategyJson["maxParallel"] != null) + { + newStrategy.maxParallel = strategyJson["maxParallel"].ToString(); + } + if (strategyJson["runOnce"] != null) + { + newStrategy.runOnce = ProcessRunOnceStrategy(strategyJson["runOnce"]); + } + if (strategyJson["canary"] != null) + { + newStrategy.canary = ProcessCanaryStrategy(strategyJson["canary"]); + } + if (strategyJson["rolling"] != null) + { + newStrategy.rolling = ProcessRollingStrategy(strategyJson["rolling"]); + } + return newStrategy; + } + else + { + return null; + } + } + + private RunOnce ProcessRunOnceStrategy(JToken strategyJson) + { + RunOnce runOnce = new RunOnce(); + if (strategyJson["preDeploy"] != null) + { + runOnce.preDeploy = ProcessDeploy(strategyJson["preDeploy"]); + } + if (strategyJson["deploy"] != null) + { + runOnce.deploy = ProcessDeploy(strategyJson["deploy"]); + } + if (strategyJson["routeTraffic"] != null) + { + runOnce.routeTraffic = ProcessDeploy(strategyJson["routeTraffic"]); + } + if (strategyJson["postRouteTraffic"] != null) + { + runOnce.postRouteTraffic = ProcessDeploy(strategyJson["postRouteTraffic"]); + } + if (strategyJson["on"] != null) + { + runOnce.on = ProcessOn(strategyJson["on"]); + } + return runOnce; + } + + private Canary ProcessCanaryStrategy(JToken strategyJson) + { + Canary canary = new Canary(); + if (strategyJson["increments"] != null) + { + canary.increments = YamlSerialization.DeserializeYaml(strategyJson["increments"].ToString()); + } + if (strategyJson["preDeploy"] != null) + { + canary.preDeploy = ProcessDeploy(strategyJson["preDeploy"]); + } + if (strategyJson["deploy"] != null) + { + canary.deploy = ProcessDeploy(strategyJson["deploy"]); + } + if (strategyJson["routeTraffic"] != null) + { + canary.routeTraffic = ProcessDeploy(strategyJson["routeTraffic"]); + } + if (strategyJson["postRouteTraffic"] != null) + { + canary.postRouteTraffic = ProcessDeploy(strategyJson["postRouteTraffic"]); + } + if (strategyJson["on"] != null) + { + canary.on = ProcessOn(strategyJson["on"]); + } + return canary; + } + + private Rolling ProcessRollingStrategy(JToken strategyJson) + { + Rolling rolling = new Rolling(); + if (strategyJson["preDeploy"] != null) + { + rolling.preDeploy = ProcessDeploy(strategyJson["preDeploy"]); + } + if (strategyJson["maxParallel"] != null) + { + rolling.maxParallel = (int)(strategyJson["maxParallel"]); + } + if (strategyJson["preDeploy"] != null) + { + rolling.preDeploy = ProcessDeploy(strategyJson["preDeploy"]); + } + if (strategyJson["deploy"] != null) + { + rolling.deploy = ProcessDeploy(strategyJson["deploy"]); + } + if (strategyJson["routeTraffic"] != null) + { + rolling.routeTraffic = ProcessDeploy(strategyJson["routeTraffic"]); + } + if (strategyJson["postRouteTraffic"] != null) + { + rolling.postRouteTraffic = ProcessDeploy(strategyJson["postRouteTraffic"]); + } + if (strategyJson["on"] != null) + { + rolling.on = ProcessOn(strategyJson["on"]); + } + + return rolling; + } + + private Deploy ProcessDeploy(JToken deployJson) + { + GeneralProcessing gp = new GeneralProcessing(_verbose); + Deploy deploy = new Deploy(); + if (deployJson["pool"] != null) + { + deploy.pool = gp.ProcessPoolV2(deployJson["pool"].ToString()); + } + if (deployJson["steps"] != null) + { + try + { + deploy.steps = YamlSerialization.DeserializeYaml(deployJson["steps"].ToString()); + } + catch (Exception ex) + { + ConversionUtility.WriteLine($"DeserializeYaml(ProcessDeploy[\"steps\"].ToString() swallowed an exception: " + ex.Message, _verbose); + } + } + + return deploy; + } + + private On ProcessOn(JToken onJson) + { + On on = new On(); + if (onJson["success"] != null) + { + on.success = ProcessDeploy(onJson["success"]); + } + if (onJson["failure"] != null) + { + on.failure = ProcessDeploy(onJson["failure"]); + } + return on; + } + + } +} diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/CompletePipelineTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/CompletePipelineTests.cs index bf8ed796..449731a6 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/CompletePipelineTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/CompletePipelineTests.cs @@ -1182,7 +1182,7 @@ echo Write your commands here string expected = @" #Note: Error! This step does not have a conversion path yet: IISWebAppDeploymentOnMachineGroup@0 #Note: Error! This step does not have a conversion path yet: IISWebAppManagementOnMachineGroup@0 -#Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet +#Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps #Note: This is a third party action: https://github.com/warrenbuckley/Setup-Nuget on: push: @@ -1207,7 +1207,7 @@ echo Write your commands here with: path: ${{ github.workspace }} Deploy_Stage_job1: - # 'Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet' + # 'Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps' environment: name: windows-server env: @@ -1448,9 +1448,9 @@ public void SSDeploymentPipelineTest() //Assert string expected = @" -#Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet -#Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet -#Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet +#Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps +#Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps +#Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps env: applicationInsightsApiKey: ${{ env.ApplicationInsights--APIKeyDev }} applicationInsightsApplicationId: ${{ env.ApplicationInsights--ApplicationIdDev }} @@ -1489,7 +1489,7 @@ public void SSDeploymentPipelineTest() websiteUrl: https://myapp-${{ env.prLC }}-eu-web.azurewebsites.net/ jobs: DeployTests1: - # 'Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet' + # 'Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps' name: Deploy functional tests to ${{ env.environment }} job runs-on: ${{ env.vmImage }} environment: @@ -1501,7 +1501,7 @@ public void SSDeploymentPipelineTest() name: drop path: ${{ github.workspace }} DeployTests2: - # 'Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet' + # 'Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps' name: Deploy functional tests to ${{ env.environment }} job runs-on: ${{ env.vmImage }} environment: @@ -1513,7 +1513,7 @@ public void SSDeploymentPipelineTest() name: drop path: ${{ github.workspace }} DeployTests3: - # 'Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet' + # 'Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps' name: Deploy functional tests to ${{ env.environment }} job runs-on: ${{ env.vmImage }} needs: diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/ConditionTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/ConditionTests.cs index 6e34c180..88ee5f68 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/ConditionTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/ConditionTests.cs @@ -363,5 +363,33 @@ public void ComplexTripleSplitWithDoubleNestedBracketsTest() Assert.AreEqual(3, results.Count); } + [TestMethod] + public void NoConditionTest() + { + //Arrange + string condition = ""; + + //Act + string result = ConditionsProcessing.TranslateConditions(condition); + + //Assert + string expected = null; + Assert.AreEqual(expected, result); + } + + [TestMethod] + public void TestConditionTest() + { + //Arrange + string condition = "Test()"; // (doesn't exist as a condition) + + //Act + string result = ConditionsProcessing.TranslateConditions(condition); + + //Assert + string expected = ""; + Assert.AreEqual(expected, result); + } + } } diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs index bac8e3bd..244ce9ec 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs @@ -142,7 +142,7 @@ public void StrategyWithMaxParallelOnlyTest() } [TestMethod] - public void StrategyRunOnceDeploymentTest() + public void StrategyRunOnceSimpleEnvironmentDeploymentTest() { //Arrange Conversion conversion = new Conversion(); @@ -169,10 +169,10 @@ public void StrategyRunOnceDeploymentTest() //Assert string expected = @" -#Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet +#Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps jobs: DeployInfrastructure: - # 'Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet' + # 'Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps' name: Deploy job runs-on: windows-latest environment: @@ -219,10 +219,10 @@ public void StrategyRunOnceWithComplexEnvironmentsDeploymentTest() //Assert string expected = @" -#Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet +#Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps jobs: DeployInfrastructure: - # 'Note: Azure DevOps strategy>runOnce>deploy does not have an equivalent in GitHub Actions yet' + # 'Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps' name: Deploy job runs-on: windows-latest environment: @@ -233,11 +233,54 @@ public void StrategyRunOnceWithComplexEnvironmentsDeploymentTest() shell: powershell"; expected = UtilityTests.TrimNewLines(expected); Assert.AreEqual(expected, gitHubOutput.actionsYaml); - + } [TestMethod] - public void StrategyRollingWithComplexEnvironmentsDeploymentTest() + public void StrategyRunOnceDeploymentTest() + { + //Arrange + Conversion conversion = new Conversion(); + string input = @" +jobs: + # Track deployments on the environment. +- deployment: DeployWeb + displayName: deploy Web App + pool: + vmImage: 'Ubuntu-16.04' + # Creates an environment if it doesn't exist. + environment: 'smarthotel-dev' + strategy: + # Default deployment strategy, more coming... + runOnce: + deploy: + steps: + - script: echo my first deployment +"; + + //Act + ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineToGitHubAction(input); + + //Assert + string expected = @" +#Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps +jobs: + DeployWeb: + # 'Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps' + name: deploy Web App + runs-on: Ubuntu-16.04 + environment: + name: smarthotel-dev + steps: + - run: echo my first deployment +"; + expected = UtilityTests.TrimNewLines(expected); + Assert.AreEqual(expected, gitHubOutput.actionsYaml); + + } + + [TestMethod] + public void StrategyRollingDeploymentTest() { //Arrange Conversion conversion = new Conversion(); @@ -281,13 +324,20 @@ public void StrategyRollingWithComplexEnvironmentsDeploymentTest() ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineToGitHubAction(input); //Assert - //TODO: Process the rest of the steps string expected = @" +#Note: Error! This step does not have a conversion path yet: IISWebAppDeploymentOnMachineGroup@0 +#Note: Azure DevOps strategy>rolling does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps jobs: VMDeploy: + # 'Note: Azure DevOps strategy>rolling does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps' name: web environment: name: smarthotel-dev + steps: + - # 'Note: Error! This step does not have a conversion path yet: IISWebAppDeploymentOnMachineGroup@0' + name: Deploy application to Website + run: 'Write-Host Note: Error! This step does not have a conversion path yet: IISWebAppDeploymentOnMachineGroup@0 #task: IISWebAppDeploymentOnMachineGroup@0#displayName: Deploy application to Website#inputs:# websitename: Default Web Site# package: ${{ env.Pipeline.Workspace }}/drop/**/*.zip' + shell: powershell "; expected = UtilityTests.TrimNewLines(expected); Assert.AreEqual(expected, gitHubOutput.actionsYaml); @@ -295,7 +345,7 @@ public void StrategyRollingWithComplexEnvironmentsDeploymentTest() } [TestMethod] - public void StrategyCanaryWithComplexEnvironmentsDeploymentTest() + public void StrategyCanaryDeploymentTest() { //Arrange Conversion conversion = new Conversion(); @@ -305,7 +355,7 @@ public void StrategyCanaryWithComplexEnvironmentsDeploymentTest() environment: smarthotel-dev.bookings pool: name: smarthotel-devPool - strategy: + strategy: canary: increments: [10,20] preDeploy: @@ -330,13 +380,16 @@ public void StrategyCanaryWithComplexEnvironmentsDeploymentTest() ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineToGitHubAction(input); //Assert - //TODO: Process the rest of the steps string expected = @" +#Note: Azure DevOps strategy>canary does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps jobs: VMDeploy: + # 'Note: Azure DevOps strategy>canary does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps' runs-on: smarthotel-devPool environment: name: smarthotel-dev.bookings + steps: + - run: echo deploy updates... "; expected = UtilityTests.TrimNewLines(expected); Assert.AreEqual(expected, gitHubOutput.actionsYaml); From a798bb93320360813035a6087d5c78c5287940a5 Mon Sep 17 00:00:00 2001 From: Sam Smith Date: Mon, 21 Dec 2020 17:55:36 -0500 Subject: [PATCH 5/8] Improved code coverage --- .../StrategyTests.cs | 75 +++++++++++-------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs index 244ce9ec..4f648ee8 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs @@ -253,9 +253,27 @@ public void StrategyRunOnceDeploymentTest() strategy: # Default deployment strategy, more coming... runOnce: + preDeploy: + steps: + - download: current + artifact: drop + - script: echo initialize, cleanup, backup, install certs deploy: steps: - - script: echo my first deployment + - script: echo Deploy application to Website + routeTraffic: + steps: + - script: echo routing traffic + postRouteTraffic: + steps: + - script: echo health check post-route traffic + on: + failure: + steps: + - script: echo Restore from backup! This is on failure + success: + steps: + - script: echo Notify! This is on success "; //Act @@ -272,7 +290,7 @@ public void StrategyRunOnceDeploymentTest() environment: name: smarthotel-dev steps: - - run: echo my first deployment + - run: echo Deploy application to Website "; expected = UtilityTests.TrimNewLines(expected); Assert.AreEqual(expected, gitHubOutput.actionsYaml); @@ -301,11 +319,7 @@ public void StrategyRollingDeploymentTest() - script: echo initialize, cleanup, backup, install certs deploy: steps: - - task: IISWebAppDeploymentOnMachineGroup@0 - displayName: 'Deploy application to Website' - inputs: - WebSiteName: 'Default Web Site' - Package: '$(Pipeline.Workspace)/drop/**/*.zip' + - script: echo Deploy application to Website routeTraffic: steps: - script: echo routing traffic @@ -325,7 +339,6 @@ public void StrategyRollingDeploymentTest() //Assert string expected = @" -#Note: Error! This step does not have a conversion path yet: IISWebAppDeploymentOnMachineGroup@0 #Note: Azure DevOps strategy>rolling does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps jobs: VMDeploy: @@ -334,10 +347,7 @@ public void StrategyRollingDeploymentTest() environment: name: smarthotel-dev steps: - - # 'Note: Error! This step does not have a conversion path yet: IISWebAppDeploymentOnMachineGroup@0' - name: Deploy application to Website - run: 'Write-Host Note: Error! This step does not have a conversion path yet: IISWebAppDeploymentOnMachineGroup@0 #task: IISWebAppDeploymentOnMachineGroup@0#displayName: Deploy application to Website#inputs:# websitename: Default Web Site# package: ${{ env.Pipeline.Workspace }}/drop/**/*.zip' - shell: powershell + - run: echo Deploy application to Website "; expected = UtilityTests.TrimNewLines(expected); Assert.AreEqual(expected, gitHubOutput.actionsYaml); @@ -358,23 +368,28 @@ public void StrategyCanaryDeploymentTest() strategy: canary: increments: [10,20] - preDeploy: - steps: - - script: initialize, cleanup.... - deploy: - steps: - - script: echo deploy updates... - postRouteTraffic: - pool: server - steps: - - script: echo monitor application health... - on: - failure: - steps: - - script: echo clean-up, rollback... - success: - steps: - - script: echo checks passed, notify... "; + preDeploy: + steps: + - download: current + artifact: drop + - script: echo initialize, cleanup, backup, install certs + deploy: + steps: + - script: echo Deploy application to Website + routeTraffic: + steps: + - script: echo routing traffic + postRouteTraffic: + steps: + - script: echo health check post-route traffic + on: + failure: + steps: + - script: echo Restore from backup! This is on failure + success: + steps: + - script: echo Notify! This is on success +"; //Act ConversionResponse gitHubOutput = conversion.ConvertAzurePipelineToGitHubAction(input); @@ -389,7 +404,7 @@ public void StrategyCanaryDeploymentTest() environment: name: smarthotel-dev.bookings steps: - - run: echo deploy updates... + - run: echo Deploy application to Website "; expected = UtilityTests.TrimNewLines(expected); Assert.AreEqual(expected, gitHubOutput.actionsYaml); From eba386019ec4d8e6e3e39bb5dccd6f4a8afed502 Mon Sep 17 00:00:00 2001 From: Sam Smith Date: Mon, 21 Dec 2020 18:13:50 -0500 Subject: [PATCH 6/8] Improving code coverage --- .../JobProcessing.cs | 6 +++--- .../StrategyProcessing.cs | 21 +++++++++++++++---- .../StrategyTests.cs | 6 ++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs index c4f8b86b..53b62413 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs @@ -54,7 +54,7 @@ public GitHubActions.Job ProcessJob(AzurePipelines.Job job, AzurePipelines.Resou //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); + newJob.steps = sp.AddSupportingSteps(job.strategy.runOnce.deploy.steps, false); } //TODO: There is currently no conversion path for runOnce strategy newJob.job_message += "Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps"; @@ -66,7 +66,7 @@ public GitHubActions.Job ProcessJob(AzurePipelines.Job job, AzurePipelines.Resou //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?.canary?.deploy?.steps, false); + newJob.steps = sp.AddSupportingSteps(job.strategy.canary.deploy.steps, false); } //TODO: There is currently no conversion path for runOnce strategy newJob.job_message += "Note: Azure DevOps strategy>canary does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps"; @@ -78,7 +78,7 @@ public GitHubActions.Job ProcessJob(AzurePipelines.Job job, AzurePipelines.Resou //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?.rolling?.deploy?.steps, false); + newJob.steps = sp.AddSupportingSteps(job.strategy.rolling.deploy.steps, false); } //TODO: There is currently no conversion path for runOnce strategy newJob.job_message += "Note: Azure DevOps strategy>rolling does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps"; diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs index ff679a4d..5ec231ed 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs @@ -3,6 +3,7 @@ using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; +using System.Runtime.Serialization; namespace AzurePipelinesToGitHubActionsConverter.Core.PipelinesToActionsConversion { @@ -58,6 +59,7 @@ private RunOnce ProcessRunOnceStrategy(JToken strategyJson) if (strategyJson["preDeploy"] != null) { runOnce.preDeploy = ProcessDeploy(strategyJson["preDeploy"]); + ConversionUtility.WriteLine("Note that RunOnce>preDeploy isn't currently processed: " + runOnce.preDeploy.ToString(), _verbose); } if (strategyJson["deploy"] != null) { @@ -66,14 +68,17 @@ private RunOnce ProcessRunOnceStrategy(JToken strategyJson) if (strategyJson["routeTraffic"] != null) { runOnce.routeTraffic = ProcessDeploy(strategyJson["routeTraffic"]); + ConversionUtility.WriteLine("Note that RunOnce>preDeploy isn't currently processed: " + runOnce.preDeploy.ToString(), _verbose); } if (strategyJson["postRouteTraffic"] != null) { runOnce.postRouteTraffic = ProcessDeploy(strategyJson["postRouteTraffic"]); + ConversionUtility.WriteLine("Note that RunOnce>preDeploy isn't currently processed: " + runOnce.preDeploy.ToString(), _verbose); } if (strategyJson["on"] != null) { runOnce.on = ProcessOn(strategyJson["on"]); + ConversionUtility.WriteLine("Note that RunOnce>on isn't currently processed: " + runOnce.on.ToString(), _verbose); } return runOnce; } @@ -84,10 +89,12 @@ private Canary ProcessCanaryStrategy(JToken strategyJson) if (strategyJson["increments"] != null) { canary.increments = YamlSerialization.DeserializeYaml(strategyJson["increments"].ToString()); + ConversionUtility.WriteLine("Note that canary>increments isn't currently processed: " + canary.increments.ToString(), _verbose); } if (strategyJson["preDeploy"] != null) { canary.preDeploy = ProcessDeploy(strategyJson["preDeploy"]); + ConversionUtility.WriteLine("Note that canary>preDeploy isn't currently processed: " + canary.preDeploy.ToString(), _verbose); } if (strategyJson["deploy"] != null) { @@ -96,14 +103,17 @@ private Canary ProcessCanaryStrategy(JToken strategyJson) if (strategyJson["routeTraffic"] != null) { canary.routeTraffic = ProcessDeploy(strategyJson["routeTraffic"]); + ConversionUtility.WriteLine("Note that canary>routeTraffic isn't currently processed: " + canary.routeTraffic.ToString(), _verbose); } if (strategyJson["postRouteTraffic"] != null) { canary.postRouteTraffic = ProcessDeploy(strategyJson["postRouteTraffic"]); + ConversionUtility.WriteLine("Note that canary>postRouteTraffic isn't currently processed: " + canary.postRouteTraffic.ToString(), _verbose); } if (strategyJson["on"] != null) { canary.on = ProcessOn(strategyJson["on"]); + ConversionUtility.WriteLine("Note that canary>on isn't currently processed: " + canary.on.ToString(), _verbose); } return canary; } @@ -111,17 +121,15 @@ private Canary ProcessCanaryStrategy(JToken strategyJson) private Rolling ProcessRollingStrategy(JToken strategyJson) { Rolling rolling = new Rolling(); - if (strategyJson["preDeploy"] != null) - { - rolling.preDeploy = ProcessDeploy(strategyJson["preDeploy"]); - } if (strategyJson["maxParallel"] != null) { rolling.maxParallel = (int)(strategyJson["maxParallel"]); + ConversionUtility.WriteLine("Note that rolling>maxParallel isn't currently processed: " + rolling.maxParallel.ToString(), _verbose); } if (strategyJson["preDeploy"] != null) { rolling.preDeploy = ProcessDeploy(strategyJson["preDeploy"]); + ConversionUtility.WriteLine("Note that rolling>preDeploy isn't currently processed: " + rolling.preDeploy.ToString(), _verbose); } if (strategyJson["deploy"] != null) { @@ -130,14 +138,17 @@ private Rolling ProcessRollingStrategy(JToken strategyJson) if (strategyJson["routeTraffic"] != null) { rolling.routeTraffic = ProcessDeploy(strategyJson["routeTraffic"]); + ConversionUtility.WriteLine("Note that rolling>routeTraffic isn't currently processed: " + rolling.routeTraffic.ToString(), _verbose); } if (strategyJson["postRouteTraffic"] != null) { rolling.postRouteTraffic = ProcessDeploy(strategyJson["postRouteTraffic"]); + ConversionUtility.WriteLine("Note that rolling>postRouteTraffic isn't currently processed: " + rolling.postRouteTraffic.ToString(), _verbose); } if (strategyJson["on"] != null) { rolling.on = ProcessOn(strategyJson["on"]); + ConversionUtility.WriteLine("Note that rolling>on isn't currently processed: " + rolling.on.ToString(), _verbose); } return rolling; @@ -172,10 +183,12 @@ private On ProcessOn(JToken onJson) if (onJson["success"] != null) { on.success = ProcessDeploy(onJson["success"]); + ConversionUtility.WriteLine("Note that success isn't currently processed: " + on.success.ToString(), _verbose); } if (onJson["failure"] != null) { on.failure = ProcessDeploy(onJson["failure"]); + ConversionUtility.WriteLine("Note that failure isn't currently processed: " + on.failure.ToString(), _verbose); } return on; } diff --git a/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs b/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs index 4f648ee8..1aafe76f 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Tests/StrategyTests.cs @@ -259,6 +259,8 @@ public void StrategyRunOnceDeploymentTest() artifact: drop - script: echo initialize, cleanup, backup, install certs deploy: + pool: + name: smarthotel-devPool steps: - script: echo Deploy application to Website routeTraffic: @@ -318,6 +320,8 @@ public void StrategyRollingDeploymentTest() artifact: drop - script: echo initialize, cleanup, backup, install certs deploy: + pool: + name: smarthotel-devPool steps: - script: echo Deploy application to Website routeTraffic: @@ -374,6 +378,8 @@ public void StrategyCanaryDeploymentTest() artifact: drop - script: echo initialize, cleanup, backup, install certs deploy: + pool: + name: smarthotel-devPool steps: - script: echo Deploy application to Website routeTraffic: From 051232240c4556ee782fd7cf71f4f1cd9971f090 Mon Sep 17 00:00:00 2001 From: Sam Smith Date: Mon, 21 Dec 2020 18:18:31 -0500 Subject: [PATCH 7/8] Improved code coverage --- .../GeneralProcessing.cs | 27 +++++++++---------- .../StrategyProcessing.cs | 4 +-- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs index 7db0bf32..19279616 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/GeneralProcessing.cs @@ -264,21 +264,18 @@ public GitHubActions.Strategy ProcessStrategy(AzurePipelines.Strategy strategy) } processedStrategy.max_parallel = strategy.maxParallel; } - if (strategy.runOnce != null) - { - //TODO: There is currently no conversion path for other strategies - ConversionUtility.WriteLine("TODO: " + strategy.runOnce, _verbose); - } - if (strategy.canary != null) - { - //TODO: There is currently no conversion path for other strategies - ConversionUtility.WriteLine("TODO: " + strategy.canary, _verbose); - } - if (strategy.rolling != null) - { - //TODO: There is currently no conversion path for other strategies - ConversionUtility.WriteLine("TODO: " + strategy.rolling, _verbose); - } + //TODO: There is currently no conversion path for other strategies //if (strategy.runOnce != null) + //{ + // ConversionUtility.WriteLine("TODO: " + strategy.runOnce, _verbose); + //} + //if (strategy.canary != null) + //{ + // ConversionUtility.WriteLine("TODO: " + strategy.canary, _verbose); + //} + //if (strategy.rolling != null) + //{ + // ConversionUtility.WriteLine("TODO: " + strategy.rolling, _verbose); + //} return processedStrategy; } else diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs index 5ec231ed..d4b53fde 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/StrategyProcessing.cs @@ -68,12 +68,12 @@ private RunOnce ProcessRunOnceStrategy(JToken strategyJson) if (strategyJson["routeTraffic"] != null) { runOnce.routeTraffic = ProcessDeploy(strategyJson["routeTraffic"]); - ConversionUtility.WriteLine("Note that RunOnce>preDeploy isn't currently processed: " + runOnce.preDeploy.ToString(), _verbose); + ConversionUtility.WriteLine("Note that RunOnce>preDeploy isn't currently processed: " + runOnce.routeTraffic.ToString(), _verbose); } if (strategyJson["postRouteTraffic"] != null) { runOnce.postRouteTraffic = ProcessDeploy(strategyJson["postRouteTraffic"]); - ConversionUtility.WriteLine("Note that RunOnce>preDeploy isn't currently processed: " + runOnce.preDeploy.ToString(), _verbose); + ConversionUtility.WriteLine("Note that RunOnce>preDeploy isn't currently processed: " + runOnce.postRouteTraffic.ToString(), _verbose); } if (strategyJson["on"] != null) { From 704ad2bfd03797239b1687146cb9a9a2d1d594a0 Mon Sep 17 00:00:00 2001 From: Sam Smith Date: Mon, 21 Dec 2020 22:49:30 -0500 Subject: [PATCH 8/8] optimized strategy a bit further --- .../JobProcessing.cs | 53 +++++++++---------- .../TriggerProcessing.cs | 9 +--- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs index 53b62413..829209ea 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/JobProcessing.cs @@ -49,49 +49,46 @@ public GitHubActions.Job ProcessJob(AzurePipelines.Job job, AzurePipelines.Resou } else if (newJob.steps == null && job.strategy?.runOnce?.deploy?.steps != null) { - if (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); - } + //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 runOnce strategy newJob.job_message += "Note: Azure DevOps strategy>runOnce does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps"; } else if (newJob.steps == null && job.strategy?.canary?.deploy?.steps != null) { - if (job.strategy?.canary?.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.canary.deploy.steps, false); - } + //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.canary.deploy.steps, false); //TODO: There is currently no conversion path for runOnce strategy newJob.job_message += "Note: Azure DevOps strategy>canary does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps"; } else if (newJob.steps == null && job.strategy?.rolling?.deploy?.steps != null) { - if (job.strategy?.rolling?.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.rolling.deploy.steps, false); - } + //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.rolling.deploy.steps, false); //TODO: There is currently no conversion path for runOnce strategy newJob.job_message += "Note: Azure DevOps strategy>rolling does not have an equivalent in GitHub Actions yet, and only the deploy steps are transferred to steps"; } - if (newJob.runs_on == null && job.strategy?.runOnce?.deploy?.pool != null) - { - if (job.strategy?.runOnce?.deploy?.pool != null) - { - newJob.runs_on = generalProcessing.ProcessPool(job.strategy?.runOnce?.deploy?.pool); - } - } + //TODO: Add this pools in for other strategies + //if (newJob.runs_on == null && job.strategy?.runOnce?.deploy?.pool != null) + //{ + // newJob.runs_on = generalProcessing.ProcessPool(job.strategy?.runOnce?.deploy?.pool); + //} + //else if (newJob.runs_on == null && job.strategy?.canary?.deploy?.pool != null) + //{ + // newJob.runs_on = generalProcessing.ProcessPool(job.strategy?.canary?.deploy?.pool); + //} + //else if (newJob.runs_on == null && job.strategy?.rolling?.deploy?.pool != null) + //{ + // newJob.runs_on = generalProcessing.ProcessPool(job.strategy?.rolling?.deploy?.pool); + //} if (job.continueOnError == true) { newJob.continue_on_error = job.continueOnError; diff --git a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/TriggerProcessing.cs b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/TriggerProcessing.cs index 15b1fe98..60c0539f 100644 --- a/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/TriggerProcessing.cs +++ b/src/AzurePipelinesToGitHubActionsConverter.Core/PipelinesToActionsConversion/TriggerProcessing.cs @@ -213,14 +213,7 @@ public GitHubActions.Trigger ProcessSchedulesV2(string schedulesYaml) Schedule[] schedules = null; if (schedulesYaml != null) { - try - { - schedules = YamlSerialization.DeserializeYaml(schedulesYaml); - } - catch (Exception ex) - { - ConversionUtility.WriteLine($"DeserializeYaml(schedulesYaml) swallowed an exception: " + ex.Message, _verbose); - } + schedules = YamlSerialization.DeserializeYaml(schedulesYaml); } //Convert the pieces to GitHub