From 4ff2c0899b92281031d9420e82e2836a471a035b Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 01:43:33 +0100 Subject: [PATCH 01/15] DATA: InstallPlaywrightTask --- .../DotNets/Tasks/InstallPlaywrightTask.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs new file mode 100644 index 0000000..607861c --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs @@ -0,0 +1,13 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks +{ + public class InstallPlaywrightTask : GithubTask + { + public override string Run { get; set; } = "dotnet tool install --global Microsoft.Playwright.CLI"; + } +} From f7a12d1d32932afc0bdf47bea865a2774344c82d Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 01:46:04 +0100 Subject: [PATCH 02/15] DATA: WorkloadUpdateTask --- .../DotNets/Tasks/WorkloadUpdateTask.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/WorkloadUpdateTask.cs diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/WorkloadUpdateTask.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/WorkloadUpdateTask.cs new file mode 100644 index 0000000..f5c02cc --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/WorkloadUpdateTask.cs @@ -0,0 +1,13 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks +{ + public class WorkloadUpdateTask : GithubTask + { + public override string Run { get; set; } = "dotnet workload update"; + } +} From 3217e27136dc07e044a6763d36b0573c80150fa8 Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 01:56:10 +0100 Subject: [PATCH 03/15] DATA: InstallPlaywrightBrowsersTask --- .../Tasks/InstallPlaywrightBrowsersTask.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightBrowsersTask.cs diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightBrowsersTask.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightBrowsersTask.cs new file mode 100644 index 0000000..a7a4c97 --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightBrowsersTask.cs @@ -0,0 +1,17 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks +{ + public class InstallPlaywrightBrowsersTask : GithubTask + { + [YamlIgnore] + public string ProjectName { get; set; } + public override string Run => $"pwsh ./{ProjectName}/bin/Debug/net9.0/playwright.ps1 install"; + } +} From 50db6ba71efd7b298607e1249e63ee92abdd43c4 Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 02:02:12 +0100 Subject: [PATCH 04/15] DATA: RunUntilFailureTask --- .../DotNets/Tasks/RunUntilFailureTask.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/RunUntilFailureTask.cs diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/RunUntilFailureTask.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/RunUntilFailureTask.cs new file mode 100644 index 0000000..85417ad --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/RunUntilFailureTask.cs @@ -0,0 +1,21 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks +{ + public class RunUntilFailureTask : GithubTask + { + [YamlMember(Order = 1, Alias = "shell", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public override string Shell => "bash"; + + [YamlMember(Order = 2, Alias = "run", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public override string Run => "for i in {1..1000};" + + @" do echo ""Run #$i""; dotnet test --no-build " + + "--verbosity normal --filter 'FullyQualifiedName!~Integrations' || exit 1; done"; + } +} From a062501ec9dfb1a9bf9c2fc7811dfb119176b145 Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 02:12:52 +0100 Subject: [PATCH 05/15] DATA: EventsV2 --- .../GithubPipelines/DotNets/EventsV2.cs | 24 +++++++++++++++++++ .../GithubPipelines/DotNets/ScheduledEvent.cs | 16 +++++++++++++ .../DotNets/WorkflowDispatchEvent.cs | 11 +++++++++ 3 files changed, 51 insertions(+) create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/EventsV2.cs create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/ScheduledEvent.cs create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/WorkflowDispatchEvent.cs diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/EventsV2.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/EventsV2.cs new file mode 100644 index 0000000..833a7e9 --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/EventsV2.cs @@ -0,0 +1,24 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class EventsV2 + { + [YamlMember(DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public PushEvent Push { get; set; } + + [YamlMember(Alias = "pull_request", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public PullRequestEvent PullRequest { get; set; } + + public ScheduledEvent[] Schedule { get; set; } + + [YamlMember(Alias = "workflow_dispatch")] + public WorkflowDispatchEvent WorkflowDispatch { get; set; } + } +} diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/ScheduledEvent.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/ScheduledEvent.cs new file mode 100644 index 0000000..4d9ecfa --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/ScheduledEvent.cs @@ -0,0 +1,16 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class ScheduledEvent + { + [YamlMember(Order = 0, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public string Cron { get; set; } + } +} diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/WorkflowDispatchEvent.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/WorkflowDispatchEvent.cs new file mode 100644 index 0000000..923778a --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/WorkflowDispatchEvent.cs @@ -0,0 +1,11 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class WorkflowDispatchEvent + { } +} From 0a3e0edb2c8fcad825611c413e68a17c38f215b9 Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 03:22:15 +0100 Subject: [PATCH 06/15] EXPOSURES: GitHubPipelineBuilder --- .../Clients/Builders/GitHubPipelineBuilder.cs | 69 ++++++++++ ADotNet/Clients/Builders/JobBuilder.cs | 118 ++++++++++++++++++ AdoNet.Tests.Console/Program.cs | 33 +++++ 3 files changed, 220 insertions(+) create mode 100644 ADotNet/Clients/Builders/GitHubPipelineBuilder.cs create mode 100644 ADotNet/Clients/Builders/JobBuilder.cs diff --git a/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs new file mode 100644 index 0000000..4852a20 --- /dev/null +++ b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs @@ -0,0 +1,69 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets; + +namespace ADotNet.Clients.Builders +{ + public class GitHubPipelineBuilder + { + private readonly GithubPipeline githubPipeline; + + private GitHubPipelineBuilder() + { + this.githubPipeline = new GithubPipeline + { + OnEvents = new Events(), + Jobs = new Dictionary() + }; + } + + public static GitHubPipelineBuilder CreateNewPipeline() => + new GitHubPipelineBuilder(); + + public GitHubPipelineBuilder SetName(string name) + { + this.githubPipeline.Name = name; + return this; + } + + public GitHubPipelineBuilder OnPush(params string[] branches) + { + this.githubPipeline.OnEvents.Push = new PushEvent + { + Branches = branches + }; + return this; + } + + public GitHubPipelineBuilder OnPullRequest(params string[] branches) + { + this.githubPipeline.OnEvents.PullRequest = new PullRequestEvent + { + Branches = branches + }; + return this; + } + + public GitHubPipelineBuilder AddJob(string jobIdentifier, Action configureJob) + { + var jobBuilder = new JobBuilder(); + configureJob(jobBuilder); + this.githubPipeline.Jobs[jobIdentifier] = jobBuilder.Build(); + return this; + } + + public void SaveToFile(string path) + { + var aDotNetClient = new ADotNetClient(); + aDotNetClient.SerializeAndWriteToFile( + this.githubPipeline, + path); + } + } +} diff --git a/ADotNet/Clients/Builders/JobBuilder.cs b/ADotNet/Clients/Builders/JobBuilder.cs new file mode 100644 index 0000000..a9641ea --- /dev/null +++ b/ADotNet/Clients/Builders/JobBuilder.cs @@ -0,0 +1,118 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using System.Collections.Generic; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks.SetupDotNetTaskV1s; + +namespace ADotNet.Clients.Builders +{ + public class JobBuilder + { + private readonly Job job; + + internal JobBuilder() + { + this.job = new Job + { + Steps = new List(), + EnvironmentVariables = null + }; + } + + public JobBuilder WithName(string name) + { + this.job.Name = name; + return this; + } + + public JobBuilder RunsOn(string machine) + { + this.job.RunsOn = machine; + return this; + } + + public JobBuilder AddEnvironmentVariable(string key, string value) + { + this.job.EnvironmentVariables ??= + new Dictionary(); + + this.job.EnvironmentVariables[key] = value; + return this; + } + + public JobBuilder AddEnvironmentVariables(Dictionary variables) + { + this.job.EnvironmentVariables ??= + new Dictionary(); + + foreach (var variable in variables) + { + this.job.EnvironmentVariables[variable.Key] = variable.Value; + } + return this; + } + + public JobBuilder AddCheckoutStep(string name = "Check out") + { + this.job.Steps.Add(new CheckoutTaskV2 { Name = name }); + return this; + } + + public JobBuilder AddSetupDotNetStep( + string version, + string stepName = "Setup Dot Net Version", + bool includePrerelease = false) + { + this.job.Steps.Add(new SetupDotNetTaskV1 + { + Name = stepName, + TargetDotNetVersion = new TargetDotNetVersion + { + DotNetVersion = version, + IncludePrerelease = includePrerelease + } + }); + return this; + } + + public JobBuilder AddRestoreStep(string name = "Restore") + { + this.job.Steps.Add(new RestoreTask { Name = name }); + return this; + } + + public JobBuilder AddBuildStep(string name = "Build") + { + this.job.Steps.Add(new DotNetBuildTask { Name = name }); + return this; + } + + public JobBuilder AddTestStep(string name = "Test", string command = null) + { + this.job.Steps.Add(new TestTask + { + Name = name, + Run = command ?? "dotnet test --no-build --verbosity normal" + }); + return this; + } + + public JobBuilder AddGenericStep(string name, string runCommand) + { + this.job.Steps.Add(new GithubTask + { + Name = name, + Run = runCommand + }); + return this; + } + + public Job Build() => this.job; + } + +} diff --git a/AdoNet.Tests.Console/Program.cs b/AdoNet.Tests.Console/Program.cs index 06d673f..bd21a24 100644 --- a/AdoNet.Tests.Console/Program.cs +++ b/AdoNet.Tests.Console/Program.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using ADotNet.Clients; +using ADotNet.Clients.Builders; using ADotNet.Models.Pipelines.AdoPipelines.AspNets; using ADotNet.Models.Pipelines.AdoPipelines.AspNets.Tasks.DotNetExecutionTasks; using ADotNet.Models.Pipelines.AdoPipelines.AspNets.Tasks.PublishBuildArtifactTasks; @@ -185,6 +186,38 @@ static void Main(string[] args) }; adoClient.SerializeAndWriteToFile(githubPipeline, "github-pipelines.yaml"); + + GitHubPipelineBuilder.CreateNewPipeline() + .SetName("Github") + .OnPush("master") + .OnPullRequest("master") + .AddJob("build", job => job + .WithName("Build") + .RunsOn(BuildMachines.WindowsLatest) + .AddEnvironmentVariable("AzureClientId", "${{ secrets.AZURECLIENTID }}") + .AddEnvironmentVariable("AzureTenantId", "${{ secrets.AZURETENANTID }}") + .AddEnvironmentVariable("AzureClientSecret", "${{ secrets.AZURECLIENTSECRET }}") + .AddEnvironmentVariable("AzureAdminName", "${{ secrets.AZUREADMINNAME }}") + .AddEnvironmentVariable("AzureAdminAccess", "${{ secrets.AZUREADMINACCESS }}") + //.AddEnvironmentVariables(new Dictionary + //{ + // { "AzureClientId", "${{ secrets.AZURECLIENTID }}" }, + // { "AzureTenantId", "${{ secrets.AZURETENANTID }}" }, + // { "AzureClientSecret", "${{ secrets.AZURECLIENTSECRET }}" }, + // { "AzureAdminName", "${{ secrets.AZUREADMINNAME }}" }, + // { "AzureAdminAccess", "${{ secrets.AZUREADMINACCESS }}" } + //}) + .AddCheckoutStep("Check Out") + .AddSetupDotNetStep( + version: "6.0.101", + includePrerelease: true) + .AddRestoreStep() + .AddBuildStep() + .AddGenericStep( + name: "Provision", + runCommand: "dotnet run --project .\\OtripleS.Api.Infrastructure.Provision\\OtripleS.Web.Api.Infrastructure.Provision.csproj")) + .SaveToFile("github-pipelines-2.yaml"); + } } } From 2f4f6eddf0114ca00de7675ef8f30faeaeca1639 Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 15:32:26 +0100 Subject: [PATCH 07/15] ShouldCreateNewPipeline -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 25 +++++++++++++++++++ .../Builders/GitHubPipelineBuilderTests.cs | 22 ++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs create mode 100644 AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs new file mode 100644 index 0000000..9ce4cde --- /dev/null +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -0,0 +1,25 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using ADotNet.Clients.Builders; +using FluentAssertions; +using Xunit; + +namespace ADotNet.Tests.Unit.Clients.Builders +{ + public partial class GitHubPipelineBuilderTests + { + [Fact] + public void ShouldCreateNewPipeline() + { + // given..when + var builder = GitHubPipelineBuilder.CreateNewPipeline(); + + // then + builder.Should().NotBeNull(); + } + } +} diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs new file mode 100644 index 0000000..8936c2b --- /dev/null +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs @@ -0,0 +1,22 @@ +// --------------------------------------------------------------------------- +// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Licensed under the MIT License. +// See License.txt in the project root for license information. +// --------------------------------------------------------------------------- + +using ADotNet.Clients.Builders; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets; + +namespace ADotNet.Tests.Unit.Clients.Builders +{ + public partial class GitHubPipelineBuilderTests + { + private static GithubPipeline GetPipeline(GitHubPipelineBuilder builder) + { + var privateField = typeof(GitHubPipelineBuilder) + .GetField("githubPipeline", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + return (GithubPipeline)privateField.GetValue(builder); + } + } +} From f9aab96bd119c7781aefd27201a0156d2c4c03e5 Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 15:34:16 +0100 Subject: [PATCH 08/15] ShouldSetPipelineName -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index 9ce4cde..a096c79 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -21,5 +21,22 @@ public void ShouldCreateNewPipeline() // then builder.Should().NotBeNull(); } + + [Fact] + public void ShouldSetPipelineName() + { + // given + string inputName = "My GitHub Pipeline"; + string expectedName = inputName; + + // when + var pipelineBuilder = GitHubPipelineBuilder.CreateNewPipeline() + .SetName(inputName); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + Assert.Equal(expectedName, actualPipeline.Name); + } } } From b68c2bee67064a9f39ac69eb401f89912f92aca2 Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 15:35:21 +0100 Subject: [PATCH 09/15] CODE RUB: Assert With Fluent Assertion --- .../Clients/Builders/GitHubPipelineBuilderTests.Logic.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index a096c79..24686c8 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -36,7 +36,8 @@ public void ShouldSetPipelineName() var actualPipeline = GetPipeline(pipelineBuilder); // then - Assert.Equal(expectedName, actualPipeline.Name); + actualPipeline.Should().NotBeNull(); + actualPipeline.Name.Should().BeEquivalentTo(expectedName); } } } From ec73e2b2a91c9226527c46465d775ca0df14081d Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 15:38:06 +0100 Subject: [PATCH 10/15] ShouldAddPushTrigger -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index 24686c8..c263e93 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -39,5 +39,23 @@ public void ShouldSetPipelineName() actualPipeline.Should().NotBeNull(); actualPipeline.Name.Should().BeEquivalentTo(expectedName); } + + [Fact] + public void ShouldAddPushTrigger() + { + // given + string[] inputBranches = { "main", "dev" }; + + // when + var pipelineBuilder = GitHubPipelineBuilder.CreateNewPipeline() + .OnPush(inputBranches); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + actualPipeline.OnEvents.Push.Should().NotBeNull(); + actualPipeline.OnEvents.Push.Branches.Should().BeEquivalentTo(inputBranches); + } + } } From a13914ac9e43288827723862afb798dc845e3674 Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 15:38:37 +0100 Subject: [PATCH 11/15] ShouldAddPullRequestTrigger -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index c263e93..3b7b956 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -57,5 +57,22 @@ public void ShouldAddPushTrigger() actualPipeline.OnEvents.Push.Branches.Should().BeEquivalentTo(inputBranches); } + + [Fact] + public void ShouldAddPullRequestTrigger() + { + // given + string[] inputBranches = { "main", "feature/*" }; + + // when + var pipelineBuilder = GitHubPipelineBuilder.CreateNewPipeline() + .OnPullRequest(inputBranches); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + actualPipeline.OnEvents.PullRequest.Should().NotBeNull(); + actualPipeline.OnEvents.PullRequest.Branches.Should().BeEquivalentTo(inputBranches); + } } } From 4805b5e1148a9c993d79349d625b43530a4e083b Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 15:40:14 +0100 Subject: [PATCH 12/15] ShouldAddJobToPipeline -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index 3b7b956..aed6cea 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -5,6 +5,8 @@ // --------------------------------------------------------------------------- using ADotNet.Clients.Builders; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; using FluentAssertions; using Xunit; @@ -74,5 +76,33 @@ public void ShouldAddPullRequestTrigger() actualPipeline.OnEvents.PullRequest.Should().NotBeNull(); actualPipeline.OnEvents.PullRequest.Branches.Should().BeEquivalentTo(inputBranches); } + + [Fact] + public void ShouldAddJobToPipeline() + { + // given + string inputJobName = "build"; + string inputRunsOn = BuildMachines.WindowsLatest; + string inputTaskName = "Restore"; + + string expectedRunsOn = inputRunsOn; + string expectedTaskName = inputTaskName; + + // when + var pipelineBuilder = GitHubPipelineBuilder.CreateNewPipeline() + .AddJob(inputJobName, job => + job.RunsOn(inputRunsOn) + .AddRestoreStep(inputTaskName)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Should().NotBeNull(); + actualJob.RunsOn.Should().Be(expectedRunsOn); + actualJob.Steps.Should().HaveCount(1); + actualJob.Steps[0].Should().BeOfType() + .Which.Name.Should().Be(expectedTaskName); + } } } From 8af445b689a011e1a00ccc164f45b3991835c12a Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 16:09:51 +0100 Subject: [PATCH 13/15] ShouldSavePipelineToFile -> FAIL --- ADotNet/ADotNet.csproj | 4 ++ .../Clients/Builders/GitHubPipelineBuilder.cs | 14 ++++--- .../GitHubPipelineBuilderTests.Logic.cs | 37 +++++++++++++++++++ .../Builders/GitHubPipelineBuilderTests.cs | 33 +++++++++++++++++ 4 files changed, 83 insertions(+), 5 deletions(-) diff --git a/ADotNet/ADotNet.csproj b/ADotNet/ADotNet.csproj index 254ea25..15dcdc8 100644 --- a/ADotNet/ADotNet.csproj +++ b/ADotNet/ADotNet.csproj @@ -86,4 +86,8 @@ + + + + diff --git a/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs index 4852a20..5c9cff4 100644 --- a/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs +++ b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs @@ -13,18 +13,23 @@ namespace ADotNet.Clients.Builders public class GitHubPipelineBuilder { private readonly GithubPipeline githubPipeline; + private readonly ADotNetClient aDotNetClient; - private GitHubPipelineBuilder() + internal GitHubPipelineBuilder(ADotNetClient aDotNetClient) { this.githubPipeline = new GithubPipeline { OnEvents = new Events(), Jobs = new Dictionary() }; + this.aDotNetClient = aDotNetClient; } - public static GitHubPipelineBuilder CreateNewPipeline() => - new GitHubPipelineBuilder(); + public static GitHubPipelineBuilder CreateNewPipeline() + { + var aDotNetClient = new ADotNetClient(); + return new GitHubPipelineBuilder(aDotNetClient); + } public GitHubPipelineBuilder SetName(string name) { @@ -60,8 +65,7 @@ public GitHubPipelineBuilder AddJob(string jobIdentifier, Action con public void SaveToFile(string path) { - var aDotNetClient = new ADotNetClient(); - aDotNetClient.SerializeAndWriteToFile( + this.aDotNetClient.SerializeAndWriteToFile( this.githubPipeline, path); } diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index aed6cea..faa1a65 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -8,6 +8,7 @@ using ADotNet.Models.Pipelines.GithubPipelines.DotNets; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; using FluentAssertions; +using Moq; using Xunit; namespace ADotNet.Tests.Unit.Clients.Builders @@ -104,5 +105,41 @@ public void ShouldAddJobToPipeline() actualJob.Steps[0].Should().BeOfType() .Which.Name.Should().Be(expectedTaskName); } + + [Fact] + public void ShouldSavePipelineToFile() + { + // given + string randomFileName = GetRandomFileName(); + string randomPipelineName = GetRandomString(); + GithubPipeline randomPipeline = + CreateRandomGithubPipeline(randomPipelineName); + + GithubPipeline inputPipeline = randomPipeline; + + string inputPath = randomFileName; + string inputPipelineName = randomPipelineName; + + this.aDotNetClientMock.Setup(client => + client.SerializeAndWriteToFile( + inputPipeline, + inputPath)) + .Verifiable(); + + this.gitHubPipelineBuilder.SetName(inputPipelineName); + + // when + this.gitHubPipelineBuilder.SaveToFile(inputPath); + + // then + this.aDotNetClientMock.Verify(client => + client.SerializeAndWriteToFile( + It.IsAny(), + It.IsAny()), + Times.Once); + + this.aDotNetClientMock.VerifyNoOtherCalls(); + } + } } diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs index 8936c2b..cc29e85 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs @@ -4,13 +4,28 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- +using System.IO; +using ADotNet.Clients; using ADotNet.Clients.Builders; using ADotNet.Models.Pipelines.GithubPipelines.DotNets; +using Moq; +using Tynamix.ObjectFiller; namespace ADotNet.Tests.Unit.Clients.Builders { public partial class GitHubPipelineBuilderTests { + private readonly Mock aDotNetClientMock; + private readonly GitHubPipelineBuilder gitHubPipelineBuilder; + + public GitHubPipelineBuilderTests() + { + this.aDotNetClientMock = new Mock(); + + this.gitHubPipelineBuilder = new GitHubPipelineBuilder( + aDotNetClient: aDotNetClientMock.Object); + } + private static GithubPipeline GetPipeline(GitHubPipelineBuilder builder) { var privateField = typeof(GitHubPipelineBuilder) @@ -18,5 +33,23 @@ private static GithubPipeline GetPipeline(GitHubPipelineBuilder builder) return (GithubPipeline)privateField.GetValue(builder); } + + private static string GetRandomString() => + new MnemonicString(wordCount: GetRandomNumber()).GetValue(); + + private static int GetRandomNumber() => + new IntRange(min: 2, max: 10).GetValue(); + + private static string GetRandomFileName() => + Path.GetRandomFileName(); + + private static GithubPipeline CreateRandomGithubPipeline(string name) => + CreateGithubPipelineFiller(name).Create(); + + private static GithubPipeline CreateRandomGithubPipeline() => + CreateGithubPipelineFiller(name: GetRandomString()).Create(); + + private static Filler CreateGithubPipelineFiller(string name) => + new Filler(); } } From 08fc22c5e66d92adfb18167a9a577c59a089cc0f Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Fri, 24 Jan 2025 16:10:52 +0100 Subject: [PATCH 14/15] ShouldSavePipelineToFile -> PASS --- ADotNet/Clients/ADotNetClient.cs | 2 +- ADotNet/Clients/Builders/GitHubPipelineBuilder.cs | 4 ++-- .../Clients/Builders/GitHubPipelineBuilderTests.cs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ADotNet/Clients/ADotNetClient.cs b/ADotNet/Clients/ADotNetClient.cs index a19fb24..8692500 100644 --- a/ADotNet/Clients/ADotNetClient.cs +++ b/ADotNet/Clients/ADotNetClient.cs @@ -10,7 +10,7 @@ namespace ADotNet.Clients { - public class ADotNetClient + public class ADotNetClient : IADotNetClient { private readonly IBuildService buildService; diff --git a/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs index 5c9cff4..3de8bc7 100644 --- a/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs +++ b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs @@ -13,9 +13,9 @@ namespace ADotNet.Clients.Builders public class GitHubPipelineBuilder { private readonly GithubPipeline githubPipeline; - private readonly ADotNetClient aDotNetClient; + private readonly IADotNetClient aDotNetClient; - internal GitHubPipelineBuilder(ADotNetClient aDotNetClient) + internal GitHubPipelineBuilder(IADotNetClient aDotNetClient) { this.githubPipeline = new GithubPipeline { diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs index cc29e85..8ed1ebc 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs @@ -15,12 +15,12 @@ namespace ADotNet.Tests.Unit.Clients.Builders { public partial class GitHubPipelineBuilderTests { - private readonly Mock aDotNetClientMock; + private readonly Mock aDotNetClientMock; private readonly GitHubPipelineBuilder gitHubPipelineBuilder; public GitHubPipelineBuilderTests() { - this.aDotNetClientMock = new Mock(); + this.aDotNetClientMock = new Mock(); this.gitHubPipelineBuilder = new GitHubPipelineBuilder( aDotNetClient: aDotNetClientMock.Object); From 8905d97c092258ad157c93ad1cfac979d052a4c2 Mon Sep 17 00:00:00 2001 From: Mabrouk Mahdhi Date: Mon, 27 Jan 2025 18:36:35 +0100 Subject: [PATCH 15/15] CODE RUB: Fix Syntax and Clean up --- .../Clients/Builders/GitHubPipelineBuilder.cs | 16 ++++++++++------ ADotNet/Clients/Builders/JobBuilder.cs | 13 +++++++++---- AdoNet.Tests.Console/Program.cs | 19 +++++++------------ 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs index 3de8bc7..7509aea 100644 --- a/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs +++ b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs @@ -22,18 +22,21 @@ internal GitHubPipelineBuilder(IADotNetClient aDotNetClient) OnEvents = new Events(), Jobs = new Dictionary() }; + this.aDotNetClient = aDotNetClient; } public static GitHubPipelineBuilder CreateNewPipeline() { var aDotNetClient = new ADotNetClient(); + return new GitHubPipelineBuilder(aDotNetClient); } public GitHubPipelineBuilder SetName(string name) { this.githubPipeline.Name = name; + return this; } @@ -43,6 +46,7 @@ public GitHubPipelineBuilder OnPush(params string[] branches) { Branches = branches }; + return this; } @@ -52,22 +56,22 @@ public GitHubPipelineBuilder OnPullRequest(params string[] branches) { Branches = branches }; + return this; } public GitHubPipelineBuilder AddJob(string jobIdentifier, Action configureJob) { var jobBuilder = new JobBuilder(); + configureJob(jobBuilder); + this.githubPipeline.Jobs[jobIdentifier] = jobBuilder.Build(); + return this; } - public void SaveToFile(string path) - { - this.aDotNetClient.SerializeAndWriteToFile( - this.githubPipeline, - path); - } + public void SaveToFile(string path) => + this.aDotNetClient.SerializeAndWriteToFile(this.githubPipeline, path); } } diff --git a/ADotNet/Clients/Builders/JobBuilder.cs b/ADotNet/Clients/Builders/JobBuilder.cs index a9641ea..2062794 100644 --- a/ADotNet/Clients/Builders/JobBuilder.cs +++ b/ADotNet/Clients/Builders/JobBuilder.cs @@ -38,8 +38,7 @@ public JobBuilder RunsOn(string machine) public JobBuilder AddEnvironmentVariable(string key, string value) { - this.job.EnvironmentVariables ??= - new Dictionary(); + this.job.EnvironmentVariables ??= new Dictionary(); this.job.EnvironmentVariables[key] = value; return this; @@ -47,19 +46,20 @@ public JobBuilder AddEnvironmentVariable(string key, string value) public JobBuilder AddEnvironmentVariables(Dictionary variables) { - this.job.EnvironmentVariables ??= - new Dictionary(); + this.job.EnvironmentVariables ??= new Dictionary(); foreach (var variable in variables) { this.job.EnvironmentVariables[variable.Key] = variable.Value; } + return this; } public JobBuilder AddCheckoutStep(string name = "Check out") { this.job.Steps.Add(new CheckoutTaskV2 { Name = name }); + return this; } @@ -77,18 +77,21 @@ public JobBuilder AddSetupDotNetStep( IncludePrerelease = includePrerelease } }); + return this; } public JobBuilder AddRestoreStep(string name = "Restore") { this.job.Steps.Add(new RestoreTask { Name = name }); + return this; } public JobBuilder AddBuildStep(string name = "Build") { this.job.Steps.Add(new DotNetBuildTask { Name = name }); + return this; } @@ -99,6 +102,7 @@ public JobBuilder AddTestStep(string name = "Test", string command = null) Name = name, Run = command ?? "dotnet test --no-build --verbosity normal" }); + return this; } @@ -109,6 +113,7 @@ public JobBuilder AddGenericStep(string name, string runCommand) Name = name, Run = runCommand }); + return this; } diff --git a/AdoNet.Tests.Console/Program.cs b/AdoNet.Tests.Console/Program.cs index bd21a24..b6d8780 100644 --- a/AdoNet.Tests.Console/Program.cs +++ b/AdoNet.Tests.Console/Program.cs @@ -195,18 +195,13 @@ static void Main(string[] args) .WithName("Build") .RunsOn(BuildMachines.WindowsLatest) .AddEnvironmentVariable("AzureClientId", "${{ secrets.AZURECLIENTID }}") - .AddEnvironmentVariable("AzureTenantId", "${{ secrets.AZURETENANTID }}") - .AddEnvironmentVariable("AzureClientSecret", "${{ secrets.AZURECLIENTSECRET }}") - .AddEnvironmentVariable("AzureAdminName", "${{ secrets.AZUREADMINNAME }}") - .AddEnvironmentVariable("AzureAdminAccess", "${{ secrets.AZUREADMINACCESS }}") - //.AddEnvironmentVariables(new Dictionary - //{ - // { "AzureClientId", "${{ secrets.AZURECLIENTID }}" }, - // { "AzureTenantId", "${{ secrets.AZURETENANTID }}" }, - // { "AzureClientSecret", "${{ secrets.AZURECLIENTSECRET }}" }, - // { "AzureAdminName", "${{ secrets.AZUREADMINNAME }}" }, - // { "AzureAdminAccess", "${{ secrets.AZUREADMINACCESS }}" } - //}) + .AddEnvironmentVariables(new Dictionary + { + { "AzureTenantId", "${{ secrets.AZURETENANTID }}" }, + { "AzureClientSecret", "${{ secrets.AZURECLIENTSECRET }}" }, + { "AzureAdminName", "${{ secrets.AZUREADMINNAME }}" }, + { "AzureAdminAccess", "${{ secrets.AZUREADMINACCESS }}" } + }) .AddCheckoutStep("Check Out") .AddSetupDotNetStep( version: "6.0.101",