From 70fbb7f9d39f451a9e15c224cb0189f3b870e5e5 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:43:54 +0100 Subject: [PATCH 01/33] DATA: Added Credentials --- .../GithubPipelines/DotNets/Credentials.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/Credentials.cs diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Credentials.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Credentials.cs new file mode 100644 index 0000000..ab6ff84 --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Credentials.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 System; +using System.Collections.Generic; +using System.Text; +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class Credentials + { + [YamlMember(Order = 0, Alias = "username", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public string Username { get; set; } + + [YamlMember(Order = 1, Alias = "password", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public string Password { get; set; } + } +} From 780d6005018001206c929559b72fab8d6fce9d1e Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:44:25 +0100 Subject: [PATCH 02/33] DATA: Added properties to strategy --- .../Pipelines/GithubPipelines/DotNets/Strategy.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs index e5bc155..17d4920 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs @@ -11,7 +11,13 @@ namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets { public class Strategy { - [YamlMember(Alias = "matrix")] - public Dictionary> Matrix { get; set; } + [YamlMember(Order = 0, Alias = "fail-fast", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public bool? FailFast { get; set; } + + [YamlMember(Order = 1, Alias = "max-parallel", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public int? MaxParallel { get; set; } + + [YamlMember(Order = 2, Alias = "matrix", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Dictionary Matrix { get; set; } } } From c67a74f1a3b685add58bab6685f07da1fcd70455 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:44:52 +0100 Subject: [PATCH 03/33] DATA: Added Service --- .../GithubPipelines/DotNets/Service.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs new file mode 100644 index 0000000..9c35eff --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs @@ -0,0 +1,35 @@ +// --------------------------------------------------------------------------- +// 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 System.Net; +using System.Text; +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class Service + { + [YamlMember(Order = 0, Alias = "image", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public string Image { get; set; } + + [YamlMember(Order = 1, Alias = "credentials", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Credentials Credentials { get; set; } + + [YamlMember(Order = 2, Alias = "env", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Dictionary Environment { get; set; } + + [YamlMember(Order = 3, Alias = "ports", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List Ports { get; set; } + + [YamlMember(Order = 4, Alias = "volumes", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List Volumes { get; set; } + + [YamlMember(Order = 5, Alias = "options", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public string Options { get; set; } + } +} From 4cda17abe6e5a61e5c7fae46210f32b78f042fad Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:46:31 +0100 Subject: [PATCH 04/33] DATA: extended job with services --- ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs index 1d47976..a6096cb 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs @@ -53,5 +53,8 @@ public class Job [YamlMember(Order = 12, Alias = "permissions", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public virtual Dictionary Permissions { get; set; } + + [YamlMember(Order = 13, Alias = "services", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual Dictionary Services { get; set; } } } From 0eb4cef18bb5b1bc0fa955bbb41337cee88a76ca Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:50:45 +0100 Subject: [PATCH 05/33] CODERUB: Enhance JobBuilder with matrix and service configuration --- ADotNet/Clients/Builders/JobBuilder.cs | 163 ++++++++++++++++++++++++- 1 file changed, 160 insertions(+), 3 deletions(-) diff --git a/ADotNet/Clients/Builders/JobBuilder.cs b/ADotNet/Clients/Builders/JobBuilder.cs index 883f1f8..ba1128c 100644 --- a/ADotNet/Clients/Builders/JobBuilder.cs +++ b/ADotNet/Clients/Builders/JobBuilder.cs @@ -8,6 +8,7 @@ using ADotNet.Models.Pipelines.GithubPipelines.DotNets; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks.SetupDotNetTaskV5s; +using FluentAssertions.Common; namespace ADotNet.Clients.Builders { @@ -162,26 +163,182 @@ public JobBuilder AddTestStep(string name = "Test", string command = null) } /// - /// Adds a generic step to the job with a custom command. + /// Specifies the jobs that this job depends on. + /// + /// The names of the jobs that this job depends on. + /// The current instance of . + public JobBuilder DependsOn(params string[] jobNames) + { + this.job.Needs = jobNames; + + return this; + } + + /// + /// Sets a conditional expression that determines whether the job runs + /// + /// The condition for the step. + /// The current instance of . + public JobBuilder WithCondition(string condition) + { + this.job.If = condition; + + return this; + } + + /// + /// Adds an axis variable (e.g. "provider": ["sqlserver", "postgres"]) to the job's matrix strategy. + /// + /// The name of the matrix axis variable. + /// The values for the matrix axis. + /// The current instance of . + public JobBuilder AddMatrix( + string variable, + params string[] values) + { + this.job.Strategy ??= new Strategy(); + this.job.Strategy.Matrix ??= new Dictionary(); + this.job.Strategy.Matrix[variable] = new List(values); + + return this; + } + + /// + /// Adds a matrix "include" combination, adding a new configuration or extending an existing one. + /// + /// The key/value pairs describing the combination to include. + /// The current instance of . + public JobBuilder AddMatrixInclude(Dictionary include) + { + GetOrAddMatrixList("include").Add(include); + + return this; + } + + /// + /// Adds a matrix "exclude" combination, removing a matching configuration. + /// + /// The key/value pairs describing the combination to exclude. + /// The current instance of . + public JobBuilder AddMatrixExclude(Dictionary exclude) + { + GetOrAddMatrixList("exclude").Add(exclude); + + return this; + } + + /// + /// Sets whether the job's matrix strategy cancels all in-progress jobs if any matrix job fails. + /// GitHub Actions defaults to true; set this explicitly to override. + /// + /// Whether to fail fast. + /// The current instance of . + public JobBuilder WithFailFast(bool failFast) + { + this.job.Strategy ??= new Strategy(); + this.job.Strategy.FailFast = failFast; + + return this; + } + + /// + /// Sets the maximum number of jobs that can run simultaneously from the matrix strategy. + /// + /// The maximum number of parallel jobs. + /// The current instance of . + public JobBuilder WithMaxParallel(int maxParallel) + { + this.job.Strategy ??= new Strategy(); + this.job.Strategy.MaxParallel = maxParallel; + + return this; + } + + /// + /// Adds a generic run-based step, optionally with an id so later steps can reference its outputs. /// /// The name of the step. /// The command to execute for this step. + /// The id of the step. + /// The shell to use for the step. + /// The current instance of . + public JobBuilder AddGenericStep( + string name, + string runCommand, + string id = null, + string shell = null) + { + this.job.Steps.Add(new GithubTask + { + Id = id, + Name = name, + Run = runCommand, + Shell = shell + }); + + return this; + } + + /// + /// Adds a step to the job that uses a specific action (e.g. "actions/checkout@v3"), + /// optionally with an id, input parameters, and environment variables. + /// + /// The name of the step. + /// The GitHub Action to use. + /// The id of the step. + /// The input parameters for the action. + /// The environment variables for the step. /// The current instance of . - public JobBuilder AddGenericStep(string name, string runCommand) + public JobBuilder AddActionStep( + string name, + string uses, + string id = null, + Dictionary with = null, + Dictionary environmentVariables = null) { this.job.Steps.Add(new GithubTask { + Id = id, Name = name, - Run = runCommand + Uses = uses, + With = with, + EnvironmentVariables = environmentVariables }); return this; } + /// + /// Attaches a service container to the job. + /// + /// The service id, used as the key under the job's "services" map. + /// The service container definition. + /// The current instance of . + public JobBuilder AddService(string id, Service service) + { + this.job.Services ??= new Dictionary(); + this.job.Services[id] = service; + + return this; + } + /// /// Builds and returns the configured job. /// /// The configured instance. public Job Build() => this.job; + + private List> GetOrAddMatrixList(string key) + { + this.job.Strategy ??= new Strategy(); + this.job.Strategy.Matrix ??= new Dictionary(); + + if (!this.job.Strategy.Matrix.ContainsKey(key)) + { + this.job.Strategy.Matrix[key] = new List>(); + } + + return (List>)this.job.Strategy.Matrix[key]; + } } } \ No newline at end of file From 270c086131fc92de42b5c862cc150e18e00993a3 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:51:21 +0100 Subject: [PATCH 06/33] CODERUB: Added Matrix and Services sample --- AdoNet.Tests.Console/Program.cs | 80 +++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/AdoNet.Tests.Console/Program.cs b/AdoNet.Tests.Console/Program.cs index dd8dbe5..8c5fc87 100644 --- a/AdoNet.Tests.Console/Program.cs +++ b/AdoNet.Tests.Console/Program.cs @@ -219,6 +219,86 @@ static void Main(string[] args) "dotnet run --project .\\{projectName}\\{projectName}.csproj")) .SaveToFile("github-pipelines-fluent.yaml"); + + + GitHubPipelineBuilder.CreateNewPipeline() + .SetName("test-strategy-services-surface") + .OnPush("main") + .OnPullRequest("main") + + .AddJob("full_surface", job => job + .WithName("Full Strategy + Services Surface") + .RunsOn(BuildMachines.UbuntuLatest) + .AddMatrix("provider", "sqlserver", "postgres") + .AddMatrix("dotnet-version", "8.0.x", "10.0.100") + .AddMatrixInclude(new () + { + ["provider"] = "sqlserver", + ["connection_string"] = + "Server=localhost;Database=TestDb;User Id=sa;" + + "Password=Your_password123!;TrustServerCertificate=True;" + + "MultipleActiveResultSets=true;Pooling=false" + }) + .AddMatrixInclude(new () + { + ["provider"] = "postgres", + ["connection_string"] = + "Host=localhost;Database=TestDb;Username=postgres;" + + "Password=postgres;Pooling=false" + }) + .AddMatrixInclude(new () + { + ["provider"] = "postgres", + ["dotnet-version"] = "9.0.x", + ["connection_string"] = + "Host=localhost;Database=TestDb;Username=postgres;" + + "Password=postgres;Pooling=false" + }) + .AddMatrixExclude(new () + { + ["provider"] = "sqlserver", + ["dotnet-version"] = "8.0.x" + }) + .WithFailFast(false) + .WithMaxParallel(2) + // NOTE: `Credentials` is populated here purely to exercise the + // services..credentials key for coverage. It's inert against a public + // image like postgres:17 — don't copy this block into a real pipeline + // without removing it or pointing it at an actual private image. + .AddService("postgres", new Service + { + Image = "postgres:17", + Credentials = new Credentials + { + Username = "${{ secrets.REGISTRY_USERNAME }}", + Password = "${{ secrets.REGISTRY_PASSWORD }}" + }, + Environment = new Dictionary + { + ["POSTGRES_DB"] = "TestDb", + ["POSTGRES_USER"] = "postgres", + ["POSTGRES_PASSWORD"] = "postgres" + }, + Ports = new List { "5432:5432" }, + Volumes = new List + { + "pgdata:/var/lib/postgresql/data" + }, + Options = + "--health-cmd pg_isready --health-interval 10s " + + "--health-timeout 5s --health-retries 5" + }) + .AddCheckoutStep("Check out") + .AddSetupDotNetStep("${{ matrix.dotnet-version }}") + .AddRestoreStep() + .AddBuildStep() + .AddGenericStep( + name: "Test", + runCommand: + "dotnet test Test.Core.Tests.Acceptance " + + "--no-build --verbosity normal")) + + .SaveToFile("C:\\Users\\slima\\Desktop\\New folder\\github-pipelines-fluent.yaml"); } } } From 76d1015e90eac60912f21a0f5f4d3b3c7c6fb5bc Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 10:59:43 +0100 Subject: [PATCH 07/33] CODERUB: Fixed failing test --- .../Builders/GitHubPipelineBuilderTests.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs index f362b5a..ea1bf98 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.cs @@ -4,6 +4,7 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- +using System.Collections.Generic; using System.IO; using ADotNet.Clients; using ADotNet.Clients.Builders; @@ -52,7 +53,20 @@ private static GithubPipeline CreateRandomGithubPipeline(string name) => private static GithubPipeline CreateRandomGithubPipeline() => CreateGithubPipelineFiller(name: GetRandomString()).Create(); - private static Filler CreateGithubPipelineFiller(string name) => - new Filler(); + private static Filler CreateGithubPipelineFiller(string name) + { + var filler = new Filler(); + + filler.Setup() + .OnProperty(p => p.EnvironmentVariables) + .Use(() => new Dictionary + { + { GetRandomString(), GetRandomString() }, + { GetRandomString(), GetRandomString() } + }) + .OnType().Use(() => GetRandomString()); + + return filler; + } } } From 9f2633f4a848974a910aa305ef22cd94b8aaab5c Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 11:09:13 +0100 Subject: [PATCH 08/33] CPDERUB: removed unused usings --- ADotNet/Clients/Builders/JobBuilder.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/ADotNet/Clients/Builders/JobBuilder.cs b/ADotNet/Clients/Builders/JobBuilder.cs index ba1128c..513f0f6 100644 --- a/ADotNet/Clients/Builders/JobBuilder.cs +++ b/ADotNet/Clients/Builders/JobBuilder.cs @@ -8,7 +8,6 @@ using ADotNet.Models.Pipelines.GithubPipelines.DotNets; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks.SetupDotNetTaskV5s; -using FluentAssertions.Common; namespace ADotNet.Clients.Builders { From fe782cd387faeb0790c8daa8288903ae2c15dca7 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:40:32 +0100 Subject: [PATCH 09/33] CODERUB: Code Cleanup --- .../Exceptions/FailedFileSerializationException.cs | 6 +++--- .../Files/Exceptions/FailedFileServiceException.cs | 6 +++--- .../Files/Exceptions/FileDependencyException.cs | 6 +++--- .../Exceptions/FileDependencyValidationException.cs | 6 +++--- .../Files/Exceptions/FileServiceException.cs | 6 +++--- .../Files/Exceptions/FileValidationException.cs | 6 +++--- .../Files/Exceptions/InvalidFileContentException.cs | 6 +++--- .../Exceptions/InvalidFileDependencyException.cs | 6 +++--- .../Files/Exceptions/InvalidFilePathException.cs | 6 +++--- .../Exceptions/AdoValidationException.cs | 2 +- .../AdoPipelines/Exceptions/NullPipelineException.cs | 1 - .../GithubPipelines/DotNets/BuildMachines.cs | 2 +- .../DotNets/Tasks/InstallPlaywrightTask.cs | 2 +- ADotNet/Services/Builds/BuildService.Exceptions.cs | 8 ++++---- .../Foundations/Files/FileService.Exceptions.cs | 12 ++++++------ .../Foundations/Files/FileService.Validations.cs | 6 +++--- ADotNet/Services/Foundations/Files/FileService.cs | 6 +++--- ADotNet/Services/Foundations/Files/IFileService.cs | 6 +++--- .../Services/Builds/BuildServiceTests.Validations.cs | 2 +- .../Files/FileServiceTests.Exceptions.Write.cs | 6 +++--- .../Files/FileServiceTests.Logic.Write.cs | 6 +++--- .../Files/FileServiceTests.Validations.Write.cs | 6 +++--- .../Services/Foundations/Files/FileServiceTests.cs | 6 +++--- 23 files changed, 62 insertions(+), 63 deletions(-) diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FailedFileSerializationException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FailedFileSerializationException.cs index a523e16..fb4107f 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FailedFileSerializationException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FailedFileSerializationException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FailedFileServiceException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FailedFileServiceException.cs index eb3dad3..e335852 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FailedFileServiceException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FailedFileServiceException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyException.cs index 7c05b69..c7df680 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyValidationException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyValidationException.cs index 2e7711d..ecc0efa 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyValidationException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyValidationException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FileServiceException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FileServiceException.cs index a769954..f587703 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FileServiceException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FileServiceException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FileValidationException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FileValidationException.cs index c7ccd19..d9c0859 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FileValidationException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FileValidationException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileContentException.cs b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileContentException.cs index e805dda..db6b0c4 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileContentException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileContentException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileDependencyException.cs b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileDependencyException.cs index 05c7b11..473bbe7 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileDependencyException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileDependencyException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFilePathException.cs b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFilePathException.cs index c10da65..50c1f57 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFilePathException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFilePathException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 Xeptions; diff --git a/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/AdoValidationException.cs b/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/AdoValidationException.cs index da38713..ca24844 100644 --- a/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/AdoValidationException.cs +++ b/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/AdoValidationException.cs @@ -10,7 +10,7 @@ namespace ADotNet.Models.Pipelines.AdoPipelines.Exceptions { public class AdoValidationException : Exception { - public AdoValidationException(string message,Exception innerException) + public AdoValidationException(string message, Exception innerException) : base(message, innerException) { } } diff --git a/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/NullPipelineException.cs b/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/NullPipelineException.cs index c6b614f..a154f88 100644 --- a/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/NullPipelineException.cs +++ b/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/NullPipelineException.cs @@ -4,7 +4,6 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- -using System; using Xeptions; namespace ADotNet.Models.Pipelines.AdoPipelines.Exceptions diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/BuildMachines.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/BuildMachines.cs index 00b8571..45e83f9 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/BuildMachines.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/BuildMachines.cs @@ -1,5 +1,5 @@ // --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// 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. // --------------------------------------------------------------------------- diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs index fd99490..9030666 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs @@ -14,7 +14,7 @@ public class InstallPlaywrightTask : GithubTask /// /// Gets or sets the name of task. /// - public override string Name { get; set; } = "Install Microsoft.Playwright.CLI."; + public override string Name { get; set; } = "Install Microsoft.Playwright.CLI."; /// /// Gets or sets the command to execute for the task. diff --git a/ADotNet/Services/Builds/BuildService.Exceptions.cs b/ADotNet/Services/Builds/BuildService.Exceptions.cs index 84fb075..e4a2f43 100644 --- a/ADotNet/Services/Builds/BuildService.Exceptions.cs +++ b/ADotNet/Services/Builds/BuildService.Exceptions.cs @@ -67,14 +67,14 @@ private static void TryCatch(ReturningNothingFunction returningNothingFunction) throw CreateBuildServiceException(exception); } } - + private static AdoValidationException CreateAdoValidationException(Xeption innerException) { return new AdoValidationException( message: "Ado validation exception occurred, try again", innerException: innerException); } - + private static AdoDependencyValidationException CreateAdoDependencyValidationException( Exception innerException) { @@ -82,7 +82,7 @@ private static AdoDependencyValidationException CreateAdoDependencyValidationExc message: "Ado dependency validation error occurs, try again.", innerException: innerException); } - + private static AdoDependencyException CreateAdoDependencyException( Exception innerException) { @@ -90,7 +90,7 @@ private static AdoDependencyException CreateAdoDependencyException( message: "Ado dependency error occured, contact support.", innerException: innerException); } - + private static Exception CreateBuildServiceException(Exception innerException) { return new BuildServiceException( diff --git a/ADotNet/Services/Foundations/Files/FileService.Exceptions.cs b/ADotNet/Services/Foundations/Files/FileService.Exceptions.cs index 42c90c2..ab845c3 100644 --- a/ADotNet/Services/Foundations/Files/FileService.Exceptions.cs +++ b/ADotNet/Services/Foundations/Files/FileService.Exceptions.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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.Runtime.Serialization; @@ -74,7 +74,7 @@ private static FileValidationException CreateFileValidationException( message: "File validation error occurred, fix the errors and try again.", innerException: innerException); } - + private static FileDependencyValidationException CreateFileDependencyValidationException( Exception innerException) { @@ -82,7 +82,7 @@ private static FileDependencyValidationException CreateFileDependencyValidationE message: "File dependency validation error occurred, fix the errors and try again.", innerException: innerException); } - + private static FileDependencyException CreateFileDependencyException( Xeption innerException) { @@ -90,7 +90,7 @@ private static FileDependencyException CreateFileDependencyException( message: "File dependency error occurred, contact support.", innerException); } - + private static FileServiceException CreateFileServiceException( Xeption innerException) { diff --git a/ADotNet/Services/Foundations/Files/FileService.Validations.cs b/ADotNet/Services/Foundations/Files/FileService.Validations.cs index 1d3c88d..5442c94 100644 --- a/ADotNet/Services/Foundations/Files/FileService.Validations.cs +++ b/ADotNet/Services/Foundations/Files/FileService.Validations.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 ADotNet.Models.Foundations.Files.Exceptions; diff --git a/ADotNet/Services/Foundations/Files/FileService.cs b/ADotNet/Services/Foundations/Files/FileService.cs index 7136e4b..2bbec9f 100644 --- a/ADotNet/Services/Foundations/Files/FileService.cs +++ b/ADotNet/Services/Foundations/Files/FileService.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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.Brokers.IOs; diff --git a/ADotNet/Services/Foundations/Files/IFileService.cs b/ADotNet/Services/Foundations/Files/IFileService.cs index 2a311f2..67270a7 100644 --- a/ADotNet/Services/Foundations/Files/IFileService.cs +++ b/ADotNet/Services/Foundations/Files/IFileService.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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.Services.Foundations.Files { diff --git a/AdoNet.Tests.Unit/Services/Builds/BuildServiceTests.Validations.cs b/AdoNet.Tests.Unit/Services/Builds/BuildServiceTests.Validations.cs index e55a1aa..05cf7b7 100644 --- a/AdoNet.Tests.Unit/Services/Builds/BuildServiceTests.Validations.cs +++ b/AdoNet.Tests.Unit/Services/Builds/BuildServiceTests.Validations.cs @@ -41,7 +41,7 @@ private void ShouldThrowValidationExceptionOnSerializeIfPipelineIsNull() actualAdoValidationException.Should().BeEquivalentTo( expectedAdoValidationException); - + this.yamlBrokerMock.Verify(broker => broker.SerializeToYaml(It.IsAny()), Times.Never); diff --git a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Exceptions.Write.cs b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Exceptions.Write.cs index 4f6c8cd..8387631 100644 --- a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Exceptions.Write.cs +++ b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Exceptions.Write.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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.Runtime.Serialization; diff --git a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Logic.Write.cs b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Logic.Write.cs index c996401..c144a0a 100644 --- a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Logic.Write.cs +++ b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Logic.Write.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 Moq; using Xunit; diff --git a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Validations.Write.cs b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Validations.Write.cs index ae0d342..b7e762a 100644 --- a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Validations.Write.cs +++ b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Validations.Write.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 ADotNet.Models.Foundations.Files.Exceptions; diff --git a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.cs b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.cs index 91e9b1a..1ae950a 100644 --- a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.cs +++ b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------- -// Copyright (c) Hassan Habib All rights reserved. +// --------------------------------------------------------------------------- +// 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 ADotNet.Brokers.IOs; From f5eb22203c71afbf49c71fff3bb69471bd898563 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:42:41 +0100 Subject: [PATCH 10/33] DATA: Added include and exclude property With matrixV2 --- .../Pipelines/GithubPipelines/DotNets/Strategy.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs index 17d4920..eaa18ea 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs @@ -4,6 +4,7 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- +using System; using System.Collections.Generic; using YamlDotNet.Serialization; @@ -17,7 +18,17 @@ public class Strategy [YamlMember(Order = 1, Alias = "max-parallel", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public int? MaxParallel { get; set; } + [Obsolete("This property is now obsolete. Please migrate to MatrixV2.")] [YamlMember(Order = 2, Alias = "matrix", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] - public Dictionary Matrix { get; set; } + public Dictionary Matrix { get; set; } + + [YamlMember(Order = 3, Alias = "matrix", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Dictionary MatrixV2 { get; set; } + + [YamlMember(Order = 4, Alias = "include", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List> Include { get; set; } + + [YamlMember(Order = 5, Alias = "exclude", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List> Exclude { get; set; } } } From a3557efc8dfd484201f998372410ab84985bd305 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:43:26 +0100 Subject: [PATCH 11/33] CODERUB: Code cleanup --- .../Models/Pipelines/GithubPipelines/DotNets/Credentials.cs | 5 +---- ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs | 2 +- ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs | 5 +---- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Credentials.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Credentials.cs index ab6ff84..515dde2 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Credentials.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Credentials.cs @@ -1,12 +1,9 @@ -// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- // 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 System.Text; using YamlDotNet.Serialization; namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs index a6096cb..7c63869 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs @@ -1,4 +1,4 @@ -// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- // 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. diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs index 9c35eff..03397f5 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Service.cs @@ -1,13 +1,10 @@ -// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- // 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 System.Net; -using System.Text; using YamlDotNet.Serialization; namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets From b9d0b6d395489bd474a208494597d4d4f1386236 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:51:05 +0100 Subject: [PATCH 12/33] CODERUB: updated strategy to use Exclide and include and returned original overload of AddGenericStep --- ADotNet/Clients/Builders/JobBuilder.cs | 42 +++++++++++++++----------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/ADotNet/Clients/Builders/JobBuilder.cs b/ADotNet/Clients/Builders/JobBuilder.cs index 513f0f6..bb0730c 100644 --- a/ADotNet/Clients/Builders/JobBuilder.cs +++ b/ADotNet/Clients/Builders/JobBuilder.cs @@ -161,6 +161,23 @@ public JobBuilder AddTestStep(string name = "Test", string command = null) return this; } + /// + /// Adds a generic step to the job with a custom command. + /// + /// The name of the step. + /// The command to execute for this step. + /// The current instance of . + public JobBuilder AddGenericStep(string name, string runCommand) + { + this.job.Steps.Add(new GithubTask + { + Name = name, + Run = runCommand + }); + + return this; + } + /// /// Specifies the jobs that this job depends on. /// @@ -196,8 +213,8 @@ public JobBuilder AddMatrix( params string[] values) { this.job.Strategy ??= new Strategy(); - this.job.Strategy.Matrix ??= new Dictionary(); - this.job.Strategy.Matrix[variable] = new List(values); + this.job.Strategy.MatrixV2 ??= new Dictionary(); + this.job.Strategy.MatrixV2[variable] = new List(values); return this; } @@ -209,7 +226,9 @@ public JobBuilder AddMatrix( /// The current instance of . public JobBuilder AddMatrixInclude(Dictionary include) { - GetOrAddMatrixList("include").Add(include); + this.job.Strategy ??= new Strategy(); + this.job.Strategy.Include ??= new List>(); + this.job.Strategy.Include.Add(include); return this; } @@ -221,7 +240,9 @@ public JobBuilder AddMatrixInclude(Dictionary include) /// The current instance of . public JobBuilder AddMatrixExclude(Dictionary exclude) { - GetOrAddMatrixList("exclude").Add(exclude); + this.job.Strategy ??= new Strategy(); + this.job.Strategy.Exclude ??= new List>(); + this.job.Strategy.Exclude.Add(exclude); return this; } @@ -326,18 +347,5 @@ public JobBuilder AddService(string id, Service service) /// /// The configured instance. public Job Build() => this.job; - - private List> GetOrAddMatrixList(string key) - { - this.job.Strategy ??= new Strategy(); - this.job.Strategy.Matrix ??= new Dictionary(); - - if (!this.job.Strategy.Matrix.ContainsKey(key)) - { - this.job.Strategy.Matrix[key] = new List>(); - } - - return (List>)this.job.Strategy.Matrix[key]; - } } } \ No newline at end of file From 3497eb51b315caa36b203c7e381d286f1f813334 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:52:32 +0100 Subject: [PATCH 13/33] CODERUB: Removed comment and credentials --- AdoNet.Tests.Console/Program.cs | 141 +++++++++++++++----------------- 1 file changed, 64 insertions(+), 77 deletions(-) diff --git a/AdoNet.Tests.Console/Program.cs b/AdoNet.Tests.Console/Program.cs index 8c5fc87..93d18b1 100644 --- a/AdoNet.Tests.Console/Program.cs +++ b/AdoNet.Tests.Console/Program.cs @@ -221,84 +221,71 @@ static void Main(string[] args) .SaveToFile("github-pipelines-fluent.yaml"); - GitHubPipelineBuilder.CreateNewPipeline() - .SetName("test-strategy-services-surface") - .OnPush("main") - .OnPullRequest("main") - - .AddJob("full_surface", job => job - .WithName("Full Strategy + Services Surface") - .RunsOn(BuildMachines.UbuntuLatest) - .AddMatrix("provider", "sqlserver", "postgres") - .AddMatrix("dotnet-version", "8.0.x", "10.0.100") - .AddMatrixInclude(new () - { - ["provider"] = "sqlserver", - ["connection_string"] = - "Server=localhost;Database=TestDb;User Id=sa;" + - "Password=Your_password123!;TrustServerCertificate=True;" + - "MultipleActiveResultSets=true;Pooling=false" - }) - .AddMatrixInclude(new () - { - ["provider"] = "postgres", - ["connection_string"] = - "Host=localhost;Database=TestDb;Username=postgres;" + - "Password=postgres;Pooling=false" - }) - .AddMatrixInclude(new () - { - ["provider"] = "postgres", - ["dotnet-version"] = "9.0.x", - ["connection_string"] = - "Host=localhost;Database=TestDb;Username=postgres;" + - "Password=postgres;Pooling=false" - }) - .AddMatrixExclude(new () - { - ["provider"] = "sqlserver", - ["dotnet-version"] = "8.0.x" - }) - .WithFailFast(false) - .WithMaxParallel(2) - // NOTE: `Credentials` is populated here purely to exercise the - // services..credentials key for coverage. It's inert against a public - // image like postgres:17 — don't copy this block into a real pipeline - // without removing it or pointing it at an actual private image. - .AddService("postgres", new Service + GitHubPipelineBuilder.CreateNewPipeline() + .SetName("test-strategy-services-surface") + .OnPush("main") + .OnPullRequest("main") + + .AddJob("full_surface", job => job + .WithName("Full Strategy + Services Surface") + .RunsOn(BuildMachines.UbuntuLatest) + .AddMatrix("provider", "sqlserver", "postgres") + .AddMatrix("dotnet-version", "8.0.x", "10.0.100") + .AddMatrixInclude(new() + { + ["provider"] = "sqlserver", + ["connection_string"] = + "Server=localhost;Database=TestDb;User Id=sa;" + + "Password=Your_password123!;TrustServerCertificate=True;" + + "MultipleActiveResultSets=true;Pooling=false" + }) + .AddMatrixInclude(new() + { + ["provider"] = "postgres", + ["connection_string"] = + "Host=localhost;Database=TestDb;Username=postgres;" + + "Password=postgres;Pooling=false" + }) + .AddMatrixInclude(new() + { + ["provider"] = "postgres", + ["dotnet-version"] = "9.0.x", + ["connection_string"] = + "Host=localhost;Database=TestDb;Username=postgres;" + + "Password=postgres;Pooling=false" + }) + .AddMatrixExclude(new() + { + ["provider"] = "sqlserver", + ["dotnet-version"] = "8.0.x" + }) + .WithFailFast(false) + .WithMaxParallel(2) + .AddService("postgres", new Service + { + Image = "postgres:17", + Environment = new Dictionary { - Image = "postgres:17", - Credentials = new Credentials - { - Username = "${{ secrets.REGISTRY_USERNAME }}", - Password = "${{ secrets.REGISTRY_PASSWORD }}" - }, - Environment = new Dictionary - { - ["POSTGRES_DB"] = "TestDb", - ["POSTGRES_USER"] = "postgres", - ["POSTGRES_PASSWORD"] = "postgres" - }, - Ports = new List { "5432:5432" }, - Volumes = new List - { - "pgdata:/var/lib/postgresql/data" - }, - Options = - "--health-cmd pg_isready --health-interval 10s " + - "--health-timeout 5s --health-retries 5" - }) - .AddCheckoutStep("Check out") - .AddSetupDotNetStep("${{ matrix.dotnet-version }}") - .AddRestoreStep() - .AddBuildStep() - .AddGenericStep( - name: "Test", - runCommand: - "dotnet test Test.Core.Tests.Acceptance " + - "--no-build --verbosity normal")) - - .SaveToFile("C:\\Users\\slima\\Desktop\\New folder\\github-pipelines-fluent.yaml"); + ["POSTGRES_DB"] = "TestDb", + ["POSTGRES_USER"] = "postgres", + ["POSTGRES_PASSWORD"] = "postgres" + }, + Ports = new List { "5432:5432" }, + Options = + "--health-cmd pg_isready --health-interval 10s " + + "--health-timeout 5s --health-retries 5" + }) + .AddCheckoutStep("Check out") + .AddSetupDotNetStep("${{ matrix.dotnet-version }}") + .AddRestoreStep() + .AddBuildStep() + .AddGenericStep( + name: "Test", + runCommand: + "dotnet test Test.Core.Tests.Acceptance " + + "--no-build --verbosity normal")) + + .SaveToFile("C:\\Users\\slima\\Desktop\\New folder\\github-pipelines-fluent.yaml"); } } } From 795006e4068b23a520e174b7b2cfe373a9711be7 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:55:09 +0100 Subject: [PATCH 14/33] ShouldBuildMatrixWithMultipleAxesThroughPipeline -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index 65cd9bb..83583ff 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -4,6 +4,7 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- +using System.Collections.Generic; using ADotNet.Clients.Builders; using ADotNet.Models.Pipelines.GithubPipelines.DotNets; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; @@ -140,5 +141,32 @@ public void ShouldSavePipelineToFile() this.aDotNetClientMock.VerifyNoOtherCalls(); } + + [Fact] + public void ShouldBuildMatrixWithMultipleAxesThroughPipeline() + { + // given + string inputJobName = "build"; + + // when + var pipelineBuilder = this.gitHubPipelineBuilder + .AddJob(inputJobName, job => job + .AddMatrix("provider", "sqlserver", "postgres") + .AddMatrix("dotnet-version", "8.0.x", "10.0.100")); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Strategy.Should().NotBeNull(); + actualJob.Strategy.MatrixV2.Should().ContainKey("provider"); + actualJob.Strategy.MatrixV2.Should().ContainKey("dotnet-version"); + + actualJob.Strategy.MatrixV2["provider"] + .Should().BeEquivalentTo(new List { "sqlserver", "postgres" }); + + actualJob.Strategy.MatrixV2["dotnet-version"] + .Should().BeEquivalentTo(new List { "8.0.x", "10.0.100" }); + } } } From 68374ef8f8c7ebd6950aff448901c2841c805910 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:55:39 +0100 Subject: [PATCH 15/33] ShouldAppendMultipleMatrixIncludeEntriesThroughPipeline -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index 83583ff..de0d7c1 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -168,5 +168,44 @@ public void ShouldBuildMatrixWithMultipleAxesThroughPipeline() actualJob.Strategy.MatrixV2["dotnet-version"] .Should().BeEquivalentTo(new List { "8.0.x", "10.0.100" }); } + + [Fact] + public void ShouldAppendMultipleMatrixIncludeEntriesThroughPipeline() + { + // given + string inputJobName = "build"; + + var firstInclude = new Dictionary + { + ["provider"] = "sqlserver", + ["connection_string"] = GetRandomString() + }; + + var secondInclude = new Dictionary + { + ["provider"] = "postgres", + ["dotnet-version"] = "9.0.x", + ["connection_string"] = GetRandomString() + }; + + // when + var pipelineBuilder = this.gitHubPipelineBuilder + .AddJob(inputJobName, job => job + .AddMatrixInclude(firstInclude) + .AddMatrixInclude(secondInclude)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + + var actualIncludeList = + actualJob.Strategy.Include as List>; + + actualIncludeList.Should().NotBeNull(); + actualIncludeList.Should().HaveCount(2); + actualIncludeList.Should().ContainEquivalentOf(firstInclude); + actualIncludeList.Should().ContainEquivalentOf(secondInclude); + } } } From a5fa98a97c3268771deb2d24b4b3c6ea861f8635 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:56:26 +0100 Subject: [PATCH 16/33] ShouldAppendMatrixExcludeEntryThroughPipeline -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index de0d7c1..fc0b8a9 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -207,5 +207,40 @@ public void ShouldAppendMultipleMatrixIncludeEntriesThroughPipeline() actualIncludeList.Should().ContainEquivalentOf(firstInclude); actualIncludeList.Should().ContainEquivalentOf(secondInclude); } + + [Fact] + public void ShouldAppendMatrixExcludeEntryThroughPipeline() + { + // given + string inputJobName = "build"; + + var excludeEntry = new Dictionary + { + ["provider"] = "sqlserver", + ["dotnet-version"] = "8.0.x" + }; + + // when + var pipelineBuilder = this.gitHubPipelineBuilder + .AddJob(inputJobName, job => job + .AddMatrix("provider", "sqlserver", "postgres") + .AddMatrix("dotnet-version", "8.0.x", "10.0.100") + .AddMatrixExclude(excludeEntry)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + + var actualExcludeList = + actualJob.Strategy.Exclude as List>; + + actualExcludeList.Should().NotBeNull(); + actualExcludeList.Should().ContainSingle(); + actualExcludeList.Should().ContainEquivalentOf(excludeEntry); + + actualJob.Strategy.MatrixV2["provider"] + .Should().BeEquivalentTo(new List { "sqlserver", "postgres" }); + } } } From dfc742481d37f92fb5529123f481485934562187 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:56:47 +0100 Subject: [PATCH 17/33] ShouldKeepIncludeAndExcludeIndependentThroughPipeline -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index fc0b8a9..16cec15 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -242,5 +242,33 @@ public void ShouldAppendMatrixExcludeEntryThroughPipeline() actualJob.Strategy.MatrixV2["provider"] .Should().BeEquivalentTo(new List { "sqlserver", "postgres" }); } + + [Fact] + public void ShouldKeepIncludeAndExcludeIndependentThroughPipeline() + { + string inputJobName = "build"; + var includeEntry = new Dictionary { ["provider"] = "postgres" }; + var excludeEntry = new Dictionary { ["provider"] = "sqlserver" }; + + // when + var pipelineBuilder = this.gitHubPipelineBuilder + .AddJob(inputJobName, job => job + .AddMatrixInclude(includeEntry) + .AddMatrixExclude(excludeEntry)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + + var actualInclude = + actualJob.Strategy.Include; + + var actualExclude = + actualJob.Strategy.Exclude; + + actualInclude.Should().ContainSingle().Which.Should().BeEquivalentTo(includeEntry); + actualExclude.Should().ContainSingle().Which.Should().BeEquivalentTo(excludeEntry); + } } } From dae515754e53b22df0a1b11363d0f350cb898e2d Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:57:10 +0100 Subject: [PATCH 18/33] ShouldSetFailFastThroughPipeline -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index 16cec15..ef39661 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -270,5 +270,26 @@ public void ShouldKeepIncludeAndExcludeIndependentThroughPipeline() actualInclude.Should().ContainSingle().Which.Should().BeEquivalentTo(includeEntry); actualExclude.Should().ContainSingle().Which.Should().BeEquivalentTo(excludeEntry); } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ShouldSetFailFastThroughPipeline(bool inputFailFast) + { + // given + string inputJobName = "build"; + + // when + var pipelineBuilder = this.gitHubPipelineBuilder + .AddJob(inputJobName, job => job + .WithFailFast(inputFailFast)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Strategy.Should().NotBeNull(); + actualJob.Strategy.FailFast.Should().Be(inputFailFast); + } } } From a6cfdf76410139bb1fd5bd36642dfed0f5cfea59 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:57:27 +0100 Subject: [PATCH 19/33] ShouldSetMaxParallelThroughPipeline -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index ef39661..0296a1e 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -291,5 +291,25 @@ public void ShouldSetFailFastThroughPipeline(bool inputFailFast) actualJob.Strategy.Should().NotBeNull(); actualJob.Strategy.FailFast.Should().Be(inputFailFast); } + + [Fact] + public void ShouldSetMaxParallelThroughPipeline() + { + // given + string inputJobName = "build"; + int inputMaxParallel = GetRandomNumber(); + + // when + var pipelineBuilder = this.gitHubPipelineBuilder + .AddJob(inputJobName, job => job + .WithMaxParallel(inputMaxParallel)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Strategy.Should().NotBeNull(); + actualJob.Strategy.MaxParallel.Should().Be(inputMaxParallel); + } } } From e80263f09c54c2bda1c9186fe91c247e7554d873 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:57:46 +0100 Subject: [PATCH 20/33] ShouldAddServiceWithFullKeySetThroughPipeline -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index 0296a1e..6cb282f 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -311,5 +311,43 @@ public void ShouldSetMaxParallelThroughPipeline() actualJob.Strategy.Should().NotBeNull(); actualJob.Strategy.MaxParallel.Should().Be(inputMaxParallel); } + + [Fact] + public void ShouldAddServiceWithFullKeySetThroughPipeline() + { + string inputJobName = "build"; + + var inputService = new Service + { + Image = GetRandomString(), + + Credentials = new Credentials + { + Username = GetRandomString(), + Password = GetRandomString() + }, + + Environment = new Dictionary + { + [GetRandomString()] = GetRandomString() + }, + + Ports = new List { "5432:5432" }, + Volumes = new List { "pgdata:/var/lib/postgresql/data" }, + Options = GetRandomString() + }; + + // when + var pipelineBuilder = this.gitHubPipelineBuilder + .AddJob(inputJobName, job => job + .AddService("postgres", inputService)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Services.Should().ContainKey("postgres"); + actualJob.Services["postgres"].Should().BeEquivalentTo(inputService); + } } } From eb824d66d1da4090fb5c8507b2dcf84975f85fb7 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:58:03 +0100 Subject: [PATCH 21/33] ShouldOverwriteServiceWhenSameIdAddedTwiceThroughPipeline -> PASS --- .../GitHubPipelineBuilderTests.Logic.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index 6cb282f..7588c98 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -349,5 +349,27 @@ public void ShouldAddServiceWithFullKeySetThroughPipeline() actualJob.Services.Should().ContainKey("postgres"); actualJob.Services["postgres"].Should().BeEquivalentTo(inputService); } + + [Fact] + public void ShouldOverwriteServiceWhenSameIdAddedTwiceThroughPipeline() + { + // given + string inputJobName = "build"; + var firstService = new Service { Image = GetRandomString() }; + var secondService = new Service { Image = GetRandomString() }; + + // when + var pipelineBuilder = this.gitHubPipelineBuilder + .AddJob(inputJobName, job => job + .AddService("postgres", firstService) + .AddService("postgres", secondService)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Services.Should().ContainSingle(); + actualJob.Services["postgres"].Should().BeEquivalentTo(secondService); + } } } From db5c0c2dc73cfb183cddfd3d2ca7be4e1555d40f Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 12:59:23 +0100 Subject: [PATCH 22/33] Revert "CODERUB: Code Cleanup" This reverts commit fe782cd387faeb0790c8daa8288903ae2c15dca7. --- .../Exceptions/FailedFileSerializationException.cs | 6 +++--- .../Files/Exceptions/FailedFileServiceException.cs | 6 +++--- .../Files/Exceptions/FileDependencyException.cs | 6 +++--- .../Exceptions/FileDependencyValidationException.cs | 6 +++--- .../Files/Exceptions/FileServiceException.cs | 6 +++--- .../Files/Exceptions/FileValidationException.cs | 6 +++--- .../Files/Exceptions/InvalidFileContentException.cs | 6 +++--- .../Exceptions/InvalidFileDependencyException.cs | 6 +++--- .../Files/Exceptions/InvalidFilePathException.cs | 6 +++--- .../Exceptions/AdoValidationException.cs | 2 +- .../AdoPipelines/Exceptions/NullPipelineException.cs | 1 + .../GithubPipelines/DotNets/BuildMachines.cs | 2 +- .../DotNets/Tasks/InstallPlaywrightTask.cs | 2 +- ADotNet/Services/Builds/BuildService.Exceptions.cs | 8 ++++---- .../Foundations/Files/FileService.Exceptions.cs | 12 ++++++------ .../Foundations/Files/FileService.Validations.cs | 6 +++--- ADotNet/Services/Foundations/Files/FileService.cs | 6 +++--- ADotNet/Services/Foundations/Files/IFileService.cs | 6 +++--- .../Services/Builds/BuildServiceTests.Validations.cs | 2 +- .../Files/FileServiceTests.Exceptions.Write.cs | 6 +++--- .../Files/FileServiceTests.Logic.Write.cs | 6 +++--- .../Files/FileServiceTests.Validations.Write.cs | 6 +++--- .../Services/Foundations/Files/FileServiceTests.cs | 6 +++--- 23 files changed, 63 insertions(+), 62 deletions(-) diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FailedFileSerializationException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FailedFileSerializationException.cs index fb4107f..a523e16 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FailedFileSerializationException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FailedFileSerializationException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using System; using Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FailedFileServiceException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FailedFileServiceException.cs index e335852..eb3dad3 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FailedFileServiceException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FailedFileServiceException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using System; using Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyException.cs index c7df680..7c05b69 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyValidationException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyValidationException.cs index ecc0efa..2e7711d 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyValidationException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FileDependencyValidationException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using System; using Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FileServiceException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FileServiceException.cs index f587703..a769954 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FileServiceException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FileServiceException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/FileValidationException.cs b/ADotNet/Models/Foundations/Files/Exceptions/FileValidationException.cs index d9c0859..c7ccd19 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/FileValidationException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/FileValidationException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileContentException.cs b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileContentException.cs index db6b0c4..e805dda 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileContentException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileContentException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileDependencyException.cs b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileDependencyException.cs index 473bbe7..05c7b11 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileDependencyException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFileDependencyException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using System; using Xeptions; diff --git a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFilePathException.cs b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFilePathException.cs index 50c1f57..c10da65 100644 --- a/ADotNet/Models/Foundations/Files/Exceptions/InvalidFilePathException.cs +++ b/ADotNet/Models/Foundations/Files/Exceptions/InvalidFilePathException.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using Xeptions; diff --git a/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/AdoValidationException.cs b/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/AdoValidationException.cs index ca24844..da38713 100644 --- a/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/AdoValidationException.cs +++ b/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/AdoValidationException.cs @@ -10,7 +10,7 @@ namespace ADotNet.Models.Pipelines.AdoPipelines.Exceptions { public class AdoValidationException : Exception { - public AdoValidationException(string message, Exception innerException) + public AdoValidationException(string message,Exception innerException) : base(message, innerException) { } } diff --git a/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/NullPipelineException.cs b/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/NullPipelineException.cs index a154f88..c6b614f 100644 --- a/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/NullPipelineException.cs +++ b/ADotNet/Models/Pipelines/AdoPipelines/Exceptions/NullPipelineException.cs @@ -4,6 +4,7 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- +using System; using Xeptions; namespace ADotNet.Models.Pipelines.AdoPipelines.Exceptions diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/BuildMachines.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/BuildMachines.cs index 45e83f9..00b8571 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/BuildMachines.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/BuildMachines.cs @@ -1,5 +1,5 @@ // --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. // --------------------------------------------------------------------------- diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs index 9030666..fd99490 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Tasks/InstallPlaywrightTask.cs @@ -14,7 +14,7 @@ public class InstallPlaywrightTask : GithubTask /// /// Gets or sets the name of task. /// - public override string Name { get; set; } = "Install Microsoft.Playwright.CLI."; + public override string Name { get; set; } = "Install Microsoft.Playwright.CLI."; /// /// Gets or sets the command to execute for the task. diff --git a/ADotNet/Services/Builds/BuildService.Exceptions.cs b/ADotNet/Services/Builds/BuildService.Exceptions.cs index e4a2f43..84fb075 100644 --- a/ADotNet/Services/Builds/BuildService.Exceptions.cs +++ b/ADotNet/Services/Builds/BuildService.Exceptions.cs @@ -67,14 +67,14 @@ private static void TryCatch(ReturningNothingFunction returningNothingFunction) throw CreateBuildServiceException(exception); } } - + private static AdoValidationException CreateAdoValidationException(Xeption innerException) { return new AdoValidationException( message: "Ado validation exception occurred, try again", innerException: innerException); } - + private static AdoDependencyValidationException CreateAdoDependencyValidationException( Exception innerException) { @@ -82,7 +82,7 @@ private static AdoDependencyValidationException CreateAdoDependencyValidationExc message: "Ado dependency validation error occurs, try again.", innerException: innerException); } - + private static AdoDependencyException CreateAdoDependencyException( Exception innerException) { @@ -90,7 +90,7 @@ private static AdoDependencyException CreateAdoDependencyException( message: "Ado dependency error occured, contact support.", innerException: innerException); } - + private static Exception CreateBuildServiceException(Exception innerException) { return new BuildServiceException( diff --git a/ADotNet/Services/Foundations/Files/FileService.Exceptions.cs b/ADotNet/Services/Foundations/Files/FileService.Exceptions.cs index ab845c3..42c90c2 100644 --- a/ADotNet/Services/Foundations/Files/FileService.Exceptions.cs +++ b/ADotNet/Services/Foundations/Files/FileService.Exceptions.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using System; using System.Runtime.Serialization; @@ -74,7 +74,7 @@ private static FileValidationException CreateFileValidationException( message: "File validation error occurred, fix the errors and try again.", innerException: innerException); } - + private static FileDependencyValidationException CreateFileDependencyValidationException( Exception innerException) { @@ -82,7 +82,7 @@ private static FileDependencyValidationException CreateFileDependencyValidationE message: "File dependency validation error occurred, fix the errors and try again.", innerException: innerException); } - + private static FileDependencyException CreateFileDependencyException( Xeption innerException) { @@ -90,7 +90,7 @@ private static FileDependencyException CreateFileDependencyException( message: "File dependency error occurred, contact support.", innerException); } - + private static FileServiceException CreateFileServiceException( Xeption innerException) { diff --git a/ADotNet/Services/Foundations/Files/FileService.Validations.cs b/ADotNet/Services/Foundations/Files/FileService.Validations.cs index 5442c94..1d3c88d 100644 --- a/ADotNet/Services/Foundations/Files/FileService.Validations.cs +++ b/ADotNet/Services/Foundations/Files/FileService.Validations.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using System; using ADotNet.Models.Foundations.Files.Exceptions; diff --git a/ADotNet/Services/Foundations/Files/FileService.cs b/ADotNet/Services/Foundations/Files/FileService.cs index 2bbec9f..7136e4b 100644 --- a/ADotNet/Services/Foundations/Files/FileService.cs +++ b/ADotNet/Services/Foundations/Files/FileService.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using ADotNet.Brokers.IOs; diff --git a/ADotNet/Services/Foundations/Files/IFileService.cs b/ADotNet/Services/Foundations/Files/IFileService.cs index 67270a7..2a311f2 100644 --- a/ADotNet/Services/Foundations/Files/IFileService.cs +++ b/ADotNet/Services/Foundations/Files/IFileService.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- namespace ADotNet.Services.Foundations.Files { diff --git a/AdoNet.Tests.Unit/Services/Builds/BuildServiceTests.Validations.cs b/AdoNet.Tests.Unit/Services/Builds/BuildServiceTests.Validations.cs index 05cf7b7..e55a1aa 100644 --- a/AdoNet.Tests.Unit/Services/Builds/BuildServiceTests.Validations.cs +++ b/AdoNet.Tests.Unit/Services/Builds/BuildServiceTests.Validations.cs @@ -41,7 +41,7 @@ private void ShouldThrowValidationExceptionOnSerializeIfPipelineIsNull() actualAdoValidationException.Should().BeEquivalentTo( expectedAdoValidationException); - + this.yamlBrokerMock.Verify(broker => broker.SerializeToYaml(It.IsAny()), Times.Never); diff --git a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Exceptions.Write.cs b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Exceptions.Write.cs index 8387631..4f6c8cd 100644 --- a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Exceptions.Write.cs +++ b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Exceptions.Write.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using System; using System.Runtime.Serialization; diff --git a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Logic.Write.cs b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Logic.Write.cs index c144a0a..c996401 100644 --- a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Logic.Write.cs +++ b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Logic.Write.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using Moq; using Xunit; diff --git a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Validations.Write.cs b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Validations.Write.cs index b7e762a..ae0d342 100644 --- a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Validations.Write.cs +++ b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.Validations.Write.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using System; using ADotNet.Models.Foundations.Files.Exceptions; diff --git a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.cs b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.cs index 1ae950a..91e9b1a 100644 --- a/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.cs +++ b/AdoNet.Tests.Unit/Services/Foundations/Files/FileServiceTests.cs @@ -1,8 +1,8 @@ -// --------------------------------------------------------------------------- -// Copyright (c) Hassan Habib & Shri Humrudha Jagathisun All rights reserved. +// --------------------------------------------------------------- +// Copyright (c) Hassan Habib All rights reserved. // Licensed under the MIT License. // See License.txt in the project root for license information. -// --------------------------------------------------------------------------- +// --------------------------------------------------------------- using System; using ADotNet.Brokers.IOs; From 7a0eb8764ce1a6b48d03b5a660435cee47580cee Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:22:43 +0100 Subject: [PATCH 23/33] CODERUB: Code cleanup --- AdoNet.Tests.Console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AdoNet.Tests.Console/Program.cs b/AdoNet.Tests.Console/Program.cs index 93d18b1..7de718e 100644 --- a/AdoNet.Tests.Console/Program.cs +++ b/AdoNet.Tests.Console/Program.cs @@ -285,7 +285,7 @@ static void Main(string[] args) "dotnet test Test.Core.Tests.Acceptance " + "--no-build --verbosity normal")) - .SaveToFile("C:\\Users\\slima\\Desktop\\New folder\\github-pipelines-fluent.yaml"); + .SaveToFile("github-pipelines-strategy-services.yaml"); } } } From 64fe840d713272fed7f4269ccc33cbedad13615c Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:28:04 +0100 Subject: [PATCH 24/33] DATA: Added StrategyV2 and cleaned up Strategy --- .../GithubPipelines/DotNets/Strategy.cs | 18 ++--------- .../GithubPipelines/DotNets/StrategyV2.cs | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 16 deletions(-) create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/StrategyV2.cs diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs index eaa18ea..e4cb020 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Strategy.cs @@ -10,25 +10,11 @@ namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets { + [Obsolete("No longer in use. Please migrate to StrategyV2.")] public class Strategy { - [YamlMember(Order = 0, Alias = "fail-fast", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] - public bool? FailFast { get; set; } - - [YamlMember(Order = 1, Alias = "max-parallel", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] - public int? MaxParallel { get; set; } - [Obsolete("This property is now obsolete. Please migrate to MatrixV2.")] - [YamlMember(Order = 2, Alias = "matrix", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + [YamlMember(Order = 0, Alias = "matrix", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public Dictionary Matrix { get; set; } - - [YamlMember(Order = 3, Alias = "matrix", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] - public Dictionary MatrixV2 { get; set; } - - [YamlMember(Order = 4, Alias = "include", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] - public List> Include { get; set; } - - [YamlMember(Order = 5, Alias = "exclude", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] - public List> Exclude { get; set; } } } diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/StrategyV2.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/StrategyV2.cs new file mode 100644 index 0000000..1aeec8e --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/StrategyV2.cs @@ -0,0 +1,30 @@ +// --------------------------------------------------------------------------- +// 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 YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class StrategyV2 + { + [YamlMember(Order = 0, Alias = "fail-fast", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public bool? FailFast { get; set; } + + [YamlMember(Order = 1, Alias = "max-parallel", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public int? MaxParallel { get; set; } + + [YamlMember(Order = 3, Alias = "matrix", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Dictionary Matrix { get; set; } + + [YamlMember(Order = 4, Alias = "include", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List> Include { get; set; } + + [YamlMember(Order = 5, Alias = "exclude", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public List> Exclude { get; set; } + } +} From e0a65347c5a1bc99065eef69813544386d95c959 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:28:38 +0100 Subject: [PATCH 25/33] DATA: Added JobV2 and cleaned up Job --- .../Pipelines/GithubPipelines/DotNets/Job.cs | 5 +- .../GithubPipelines/DotNets/JobV2.cs | 60 +++++++++++++++++++ 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/JobV2.cs diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs index 7c63869..3e3bae5 100644 --- a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/Job.cs @@ -4,6 +4,7 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- +using System; using System.Collections.Generic; using System.ComponentModel; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; @@ -11,6 +12,7 @@ namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets { + [Obsolete("No longer in use. Please migrate to JobV2.")] public class Job { [YamlMember(Order = 0, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] @@ -53,8 +55,5 @@ public class Job [YamlMember(Order = 12, Alias = "permissions", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] public virtual Dictionary Permissions { get; set; } - - [YamlMember(Order = 13, Alias = "services", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] - public virtual Dictionary Services { get; set; } } } diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/JobV2.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/JobV2.cs new file mode 100644 index 0000000..8a840cd --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/JobV2.cs @@ -0,0 +1,60 @@ +// --------------------------------------------------------------------------- +// 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 System.ComponentModel; +using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class JobV2 + { + [YamlMember(Order = 0, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual string Name { get; set; } + + [YamlMember(Order = 1, Alias = "runs-on")] + public virtual string RunsOn { get; set; } + + [YamlMember(Order = 2, Alias = "needs", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual string[] Needs { get; set; } + + [YamlMember(Order = 3, Alias = "if", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual string If { get; set; } + + [YamlMember(Order = 4, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual string Environment { get; set; } + + [YamlMember(Order = 5, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual DefaultValues Defaults { get; set; } + + [YamlMember(Order = 6)] + public virtual List Steps { get; set; } + + [DefaultValue(0)] + [YamlMember(Order = 7, Alias = "timeout-minutes", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual int TimeoutInMinutes { get; set; } + + [YamlMember(Order = 8, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual StrategyV2 Strategy { get; set; } + + [YamlMember(Order = 9, Alias = "env", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual Dictionary EnvironmentVariables { get; set; } + + [YamlMember(Order = 10, DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual Dictionary Outputs { get; set; } + + [DefaultValue(false)] + [YamlMember(Order = 11, Alias = "continue-on-error", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual bool ContinueOnError { get; set; } + + [YamlMember(Order = 12, Alias = "permissions", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual Dictionary Permissions { get; set; } + + [YamlMember(Order = 13, Alias = "services", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public virtual Dictionary Services { get; set; } + } +} From d85f5a76725514015e9ac23f33ea1c2eceb6b783 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:29:06 +0100 Subject: [PATCH 26/33] CODERUB: Added GithubPipelineV2 --- .../DotNets/GithubPipelineV2.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 ADotNet/Models/Pipelines/GithubPipelines/DotNets/GithubPipelineV2.cs diff --git a/ADotNet/Models/Pipelines/GithubPipelines/DotNets/GithubPipelineV2.cs b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/GithubPipelineV2.cs new file mode 100644 index 0000000..3ed6cea --- /dev/null +++ b/ADotNet/Models/Pipelines/GithubPipelines/DotNets/GithubPipelineV2.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 System.Collections.Generic; +using YamlDotNet.Serialization; + +namespace ADotNet.Models.Pipelines.GithubPipelines.DotNets +{ + public class GithubPipelineV2 + { + public string Name { get; set; } + + [YamlMember(Alias = "on")] + public Events OnEvents { get; set; } + + [YamlMember(Alias = "env", DefaultValuesHandling = DefaultValuesHandling.OmitDefaults)] + public Dictionary EnvironmentVariables { get; set; } + + [YamlMember(Alias = "jobs")] + public Dictionary Jobs { get; set; } + } +} From 29f1ff7c7c34fb83d3b54427bfeb106450412cb7 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:30:45 +0100 Subject: [PATCH 27/33] CODERUB: Added GithubPipelineBuilderV2 and cleaned up GithubPipelineBuilder --- .../Clients/Builders/GitHubPipelineBuilder.cs | 1 + .../Builders/GitHubPipelineBuilderV2.cs | 108 ++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 ADotNet/Clients/Builders/GitHubPipelineBuilderV2.cs diff --git a/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs index 0d6d99c..fbb53b2 100644 --- a/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs +++ b/ADotNet/Clients/Builders/GitHubPipelineBuilder.cs @@ -13,6 +13,7 @@ namespace ADotNet.Clients.Builders /// /// Builder for creating a GitHub pipeline. /// + [Obsolete("No longer in use. Please migrate to GitHubPipelineBuilderV2.")] public class GitHubPipelineBuilder { private readonly GithubPipeline githubPipeline; diff --git a/ADotNet/Clients/Builders/GitHubPipelineBuilderV2.cs b/ADotNet/Clients/Builders/GitHubPipelineBuilderV2.cs new file mode 100644 index 0000000..5e8feb4 --- /dev/null +++ b/ADotNet/Clients/Builders/GitHubPipelineBuilderV2.cs @@ -0,0 +1,108 @@ +// --------------------------------------------------------------------------- +// 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 +{ + /// + /// Builder for creating a GitHub pipeline. + /// + public class GitHubPipelineBuilderV2 + { + private readonly GithubPipelineV2 githubPipelineV2; + private readonly IADotNetClient aDotNetClient; + + internal GitHubPipelineBuilderV2(IADotNetClient aDotNetClient) + { + this.githubPipelineV2 = new GithubPipelineV2 + { + OnEvents = new Events(), + Jobs = new Dictionary() + }; + + this.aDotNetClient = aDotNetClient; + } + + /// + /// Creates a new instance of the class + /// with a default . + /// + /// A new instance of . + public static GitHubPipelineBuilderV2 CreateNewPipeline() + { + var aDotNetClient = new ADotNetClient(); + + return new GitHubPipelineBuilderV2(aDotNetClient); + } + + /// + /// Sets the name of the GitHub pipeline. + /// + /// The name of the pipeline. + /// The current instance of . + public GitHubPipelineBuilderV2 SetName(string name) + { + this.githubPipelineV2.Name = name; + + return this; + } + + /// + /// Configures the pipeline to trigger on push events for specified branches. + /// + /// The branches to trigger on push events. + /// The current instance of . + public GitHubPipelineBuilderV2 OnPush(params string[] branches) + { + this.githubPipelineV2.OnEvents.Push = new PushEvent + { + Branches = branches + }; + + return this; + } + + /// + /// Configures the pipeline to trigger on pull request events for specified branches. + /// + /// The branches to trigger on pull request events. + /// The current instance of . + public GitHubPipelineBuilderV2 OnPullRequest(params string[] branches) + { + this.githubPipelineV2.OnEvents.PullRequest = new PullRequestEvent + { + Branches = branches + }; + + return this; + } + + /// + /// Adds a job to the GitHub pipeline. + /// + /// The unique identifier for the job. + /// The action to configure the job. + /// The current instance of . + public GitHubPipelineBuilderV2 AddJob(string jobIdentifier, Action configureJob) + { + var jobBuilder = new JobBuilderV2(); + configureJob(jobBuilder); + this.githubPipelineV2.Jobs[jobIdentifier] = jobBuilder.Build(); + + return this; + } + + /// + /// Saves the configured pipeline (yml) to the specified file path. + /// + /// The file path where the pipeline will be saved. + public void SaveToFile(string path) => + this.aDotNetClient.SerializeAndWriteToFile(this.githubPipelineV2, path); + } +} From cc36103744939b1dea3024cf1345a3d8a415ddb3 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:31:35 +0100 Subject: [PATCH 28/33] CODERUB: Added JobBuilderV2 and cleaned up JobBuilder --- ADotNet/Clients/Builders/JobBuilder.cs | 166 +---------- ADotNet/Clients/Builders/JobBuilderV2.cs | 351 +++++++++++++++++++++++ 2 files changed, 353 insertions(+), 164 deletions(-) create mode 100644 ADotNet/Clients/Builders/JobBuilderV2.cs diff --git a/ADotNet/Clients/Builders/JobBuilder.cs b/ADotNet/Clients/Builders/JobBuilder.cs index bb0730c..ad1bb02 100644 --- a/ADotNet/Clients/Builders/JobBuilder.cs +++ b/ADotNet/Clients/Builders/JobBuilder.cs @@ -4,6 +4,7 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- +using System; using System.Collections.Generic; using ADotNet.Models.Pipelines.GithubPipelines.DotNets; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; @@ -14,6 +15,7 @@ namespace ADotNet.Clients.Builders /// /// A builder to create a job for a GitHub Actions workflow. /// + [Obsolete("No longer in use. Please migrate to JobBuilderV2.")] public class JobBuilder { private readonly Job job; @@ -178,170 +180,6 @@ public JobBuilder AddGenericStep(string name, string runCommand) return this; } - /// - /// Specifies the jobs that this job depends on. - /// - /// The names of the jobs that this job depends on. - /// The current instance of . - public JobBuilder DependsOn(params string[] jobNames) - { - this.job.Needs = jobNames; - - return this; - } - - /// - /// Sets a conditional expression that determines whether the job runs - /// - /// The condition for the step. - /// The current instance of . - public JobBuilder WithCondition(string condition) - { - this.job.If = condition; - - return this; - } - - /// - /// Adds an axis variable (e.g. "provider": ["sqlserver", "postgres"]) to the job's matrix strategy. - /// - /// The name of the matrix axis variable. - /// The values for the matrix axis. - /// The current instance of . - public JobBuilder AddMatrix( - string variable, - params string[] values) - { - this.job.Strategy ??= new Strategy(); - this.job.Strategy.MatrixV2 ??= new Dictionary(); - this.job.Strategy.MatrixV2[variable] = new List(values); - - return this; - } - - /// - /// Adds a matrix "include" combination, adding a new configuration or extending an existing one. - /// - /// The key/value pairs describing the combination to include. - /// The current instance of . - public JobBuilder AddMatrixInclude(Dictionary include) - { - this.job.Strategy ??= new Strategy(); - this.job.Strategy.Include ??= new List>(); - this.job.Strategy.Include.Add(include); - - return this; - } - - /// - /// Adds a matrix "exclude" combination, removing a matching configuration. - /// - /// The key/value pairs describing the combination to exclude. - /// The current instance of . - public JobBuilder AddMatrixExclude(Dictionary exclude) - { - this.job.Strategy ??= new Strategy(); - this.job.Strategy.Exclude ??= new List>(); - this.job.Strategy.Exclude.Add(exclude); - - return this; - } - - /// - /// Sets whether the job's matrix strategy cancels all in-progress jobs if any matrix job fails. - /// GitHub Actions defaults to true; set this explicitly to override. - /// - /// Whether to fail fast. - /// The current instance of . - public JobBuilder WithFailFast(bool failFast) - { - this.job.Strategy ??= new Strategy(); - this.job.Strategy.FailFast = failFast; - - return this; - } - - /// - /// Sets the maximum number of jobs that can run simultaneously from the matrix strategy. - /// - /// The maximum number of parallel jobs. - /// The current instance of . - public JobBuilder WithMaxParallel(int maxParallel) - { - this.job.Strategy ??= new Strategy(); - this.job.Strategy.MaxParallel = maxParallel; - - return this; - } - - /// - /// Adds a generic run-based step, optionally with an id so later steps can reference its outputs. - /// - /// The name of the step. - /// The command to execute for this step. - /// The id of the step. - /// The shell to use for the step. - /// The current instance of . - public JobBuilder AddGenericStep( - string name, - string runCommand, - string id = null, - string shell = null) - { - this.job.Steps.Add(new GithubTask - { - Id = id, - Name = name, - Run = runCommand, - Shell = shell - }); - - return this; - } - - /// - /// Adds a step to the job that uses a specific action (e.g. "actions/checkout@v3"), - /// optionally with an id, input parameters, and environment variables. - /// - /// The name of the step. - /// The GitHub Action to use. - /// The id of the step. - /// The input parameters for the action. - /// The environment variables for the step. - /// The current instance of . - public JobBuilder AddActionStep( - string name, - string uses, - string id = null, - Dictionary with = null, - Dictionary environmentVariables = null) - { - this.job.Steps.Add(new GithubTask - { - Id = id, - Name = name, - Uses = uses, - With = with, - EnvironmentVariables = environmentVariables - }); - - return this; - } - - /// - /// Attaches a service container to the job. - /// - /// The service id, used as the key under the job's "services" map. - /// The service container definition. - /// The current instance of . - public JobBuilder AddService(string id, Service service) - { - this.job.Services ??= new Dictionary(); - this.job.Services[id] = service; - - return this; - } - /// /// Builds and returns the configured job. /// diff --git a/ADotNet/Clients/Builders/JobBuilderV2.cs b/ADotNet/Clients/Builders/JobBuilderV2.cs new file mode 100644 index 0000000..c269d36 --- /dev/null +++ b/ADotNet/Clients/Builders/JobBuilderV2.cs @@ -0,0 +1,351 @@ +// --------------------------------------------------------------------------- +// 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.SetupDotNetTaskV5s; + +namespace ADotNet.Clients.Builders +{ + /// + /// A builder to create a job for a GitHub Actions workflow. + /// + public class JobBuilderV2 + { + private readonly JobV2 job; + + internal JobBuilderV2() + { + this.job = new JobV2 + { + Steps = new List(), + EnvironmentVariables = null + }; + } + + /// + /// Sets the name of the job. + /// + /// The name of the job. + /// The current instance of . + public JobBuilderV2 WithName(string name) + { + this.job.Name = name; + + return this; + } + + /// + /// Specifies the machine on which the job will run. + /// + /// The machine or environment to run the job on. + /// The current instance of . + public JobBuilderV2 RunsOn(string machine) + { + this.job.RunsOn = machine; + + return this; + } + + /// + /// Adds an environment variable to the job. + /// + /// The key of the environment variable. + /// The value of the environment variable. + /// The current instance of . + public JobBuilderV2 AddEnvironmentVariable(string key, string value) + { + this.job.EnvironmentVariables ??= new Dictionary(); + + this.job.EnvironmentVariables[key] = value; + + return this; + } + + /// + /// Adds multiple environment variables to the job. + /// + /// A dictionary of environment variables to add. + /// The current instance of . + public JobBuilderV2 AddEnvironmentVariables(Dictionary variables) + { + this.job.EnvironmentVariables ??= new Dictionary(); + + foreach (var variable in variables) + { + this.job.EnvironmentVariables[variable.Key] = variable.Value; + } + + return this; + } + + /// + /// Adds a checkout step to the job. + /// + /// The name of the checkout step (default: "Check out"). + /// The current instance of . + public JobBuilderV2 AddCheckoutStep(string name = "Check out") + { + this.job.Steps.Add(new CheckoutTaskV5 { Name = name }); + + return this; + } + + /// + /// Adds a setup step for a specific .NET version to the job. + /// + /// The version of .NET to set up. + /// The name of the setup step (default: "Setup Dot Net Version"). + /// Specifies whether to include prerelease versions. + /// The current instance of . + public JobBuilderV2 AddSetupDotNetStep( + string version, + string stepName = "Setup Dot Net Version", + bool includePrerelease = false) + { + this.job.Steps.Add(new SetupDotNetTaskV5 + { + Name = stepName, + With = new TargetDotNetVersionV5 + { + DotNetVersion = version, + } + }); + + return this; + } + + /// + /// Adds a restore step to the job. + /// + /// The name of the restore step (default: "Restore"). + /// The current instance of . + public JobBuilderV2 AddRestoreStep(string name = "Restore") + { + this.job.Steps.Add(new RestoreTask { Name = name }); + + return this; + } + + /// + /// Adds a build step to the job. + /// + /// The name of the build step (default: "Build"). + /// The current instance of . + public JobBuilderV2 AddBuildStep(string name = "Build") + { + this.job.Steps.Add(new DotNetBuildTask { Name = name }); + + return this; + } + + /// + /// Adds a test step to the job. + /// + /// The name of the test step (default: "Test"). + /// The command to execute the test + /// (default: "dotnet test --no-build --verbosity normal"). + /// The current instance of . + public JobBuilderV2 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; + } + + /// + /// Adds a generic step to the job with a custom command. + /// + /// The name of the step. + /// The command to execute for this step. + /// The current instance of . + public JobBuilderV2 AddGenericStep(string name, string runCommand) + { + this.job.Steps.Add(new GithubTask + { + Name = name, + Run = runCommand + }); + + return this; + } + + /// + /// Specifies the jobs that this job depends on. + /// + /// The names of the jobs that this job depends on. + /// The current instance of . + public JobBuilderV2 DependsOn(params string[] jobNames) + { + this.job.Needs = jobNames; + + return this; + } + + /// + /// Sets a conditional expression that determines whether the job runs + /// + /// The condition for the step. + /// The current instance of . + public JobBuilderV2 WithCondition(string condition) + { + this.job.If = condition; + + return this; + } + + /// + /// Adds an axis variable (e.g. "provider": ["sqlserver", "postgres"]) to the job's matrix strategy. + /// + /// The name of the matrix axis variable. + /// The values for the matrix axis. + /// The current instance of . + public JobBuilderV2 AddMatrix( + string variable, + params string[] values) + { + this.job.Strategy ??= new StrategyV2(); + this.job.Strategy.Matrix ??= new Dictionary(); + this.job.Strategy.Matrix[variable] = new List(values); + + return this; + } + + /// + /// Adds a matrix "include" combination, adding a new configuration or extending an existing one. + /// + /// The key/value pairs describing the combination to include. + /// The current instance of . + public JobBuilderV2 AddMatrixInclude(Dictionary include) + { + this.job.Strategy ??= new StrategyV2(); + this.job.Strategy.Include ??= new List>(); + this.job.Strategy.Include.Add(include); + + return this; + } + + /// + /// Adds a matrix "exclude" combination, removing a matching configuration. + /// + /// The key/value pairs describing the combination to exclude. + /// The current instance of . + public JobBuilderV2 AddMatrixExclude(Dictionary exclude) + { + this.job.Strategy ??= new StrategyV2(); + this.job.Strategy.Exclude ??= new List>(); + this.job.Strategy.Exclude.Add(exclude); + + return this; + } + + /// + /// Sets whether the job's matrix strategy cancels all in-progress jobs if any matrix job fails. + /// GitHub Actions defaults to true; set this explicitly to override. + /// + /// Whether to fail fast. + /// The current instance of . + public JobBuilderV2 WithFailFast(bool failFast) + { + this.job.Strategy ??= new StrategyV2(); + this.job.Strategy.FailFast = failFast; + + return this; + } + + /// + /// Sets the maximum number of jobs that can run simultaneously from the matrix strategy. + /// + /// The maximum number of parallel jobs. + /// The current instance of . + public JobBuilderV2 WithMaxParallel(int maxParallel) + { + this.job.Strategy ??= new StrategyV2(); + this.job.Strategy.MaxParallel = maxParallel; + + return this; + } + + /// + /// Adds a generic run-based step, optionally with an id so later steps can reference its outputs. + /// + /// The name of the step. + /// The command to execute for this step. + /// The id of the step. + /// The shell to use for the step. + /// The current instance of . + public JobBuilderV2 AddGenericStep( + string name, + string runCommand, + string id = null, + string shell = null) + { + this.job.Steps.Add(new GithubTask + { + Id = id, + Name = name, + Run = runCommand, + Shell = shell + }); + + return this; + } + + /// + /// Adds a step to the job that uses a specific action (e.g. "actions/checkout@v3"), + /// optionally with an id, input parameters, and environment variables. + /// + /// The name of the step. + /// The GitHub Action to use. + /// The id of the step. + /// The input parameters for the action. + /// The environment variables for the step. + /// The current instance of . + public JobBuilderV2 AddActionStep( + string name, + string uses, + string id = null, + Dictionary with = null, + Dictionary environmentVariables = null) + { + this.job.Steps.Add(new GithubTask + { + Id = id, + Name = name, + Uses = uses, + With = with, + EnvironmentVariables = environmentVariables + }); + + return this; + } + + /// + /// Attaches a service container to the job. + /// + /// The service id, used as the key under the job's "services" map. + /// The service container definition. + /// The current instance of . + public JobBuilderV2 AddService(string id, Service service) + { + this.job.Services ??= new Dictionary(); + this.job.Services[id] = service; + + return this; + } + + /// + /// Builds and returns the configured job. + /// + /// The configured instance. + public JobV2 Build() => this.job; + } +} \ No newline at end of file From 47b7d80947500ccf07fce4394e412088c55837d9 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:33:33 +0100 Subject: [PATCH 29/33] CODERUB: Updated sample to use GitHubPipelineBuilderV2 --- AdoNet.Tests.Console/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AdoNet.Tests.Console/Program.cs b/AdoNet.Tests.Console/Program.cs index 7de718e..db40b29 100644 --- a/AdoNet.Tests.Console/Program.cs +++ b/AdoNet.Tests.Console/Program.cs @@ -221,7 +221,7 @@ static void Main(string[] args) .SaveToFile("github-pipelines-fluent.yaml"); - GitHubPipelineBuilder.CreateNewPipeline() + GitHubPipelineBuilderV2.CreateNewPipeline() .SetName("test-strategy-services-surface") .OnPush("main") .OnPullRequest("main") From ac56ef8cb244acab337b2c5510d6a5bc2a5526ec Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:34:49 +0100 Subject: [PATCH 30/33] CODERUB: cleaned up V1 unit test --- .../GitHubPipelineBuilderTests.Logic.cs | 230 ------------------ 1 file changed, 230 deletions(-) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index 7588c98..eaeb134 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -141,235 +141,5 @@ public void ShouldSavePipelineToFile() this.aDotNetClientMock.VerifyNoOtherCalls(); } - - [Fact] - public void ShouldBuildMatrixWithMultipleAxesThroughPipeline() - { - // given - string inputJobName = "build"; - - // when - var pipelineBuilder = this.gitHubPipelineBuilder - .AddJob(inputJobName, job => job - .AddMatrix("provider", "sqlserver", "postgres") - .AddMatrix("dotnet-version", "8.0.x", "10.0.100")); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - actualJob.Strategy.Should().NotBeNull(); - actualJob.Strategy.MatrixV2.Should().ContainKey("provider"); - actualJob.Strategy.MatrixV2.Should().ContainKey("dotnet-version"); - - actualJob.Strategy.MatrixV2["provider"] - .Should().BeEquivalentTo(new List { "sqlserver", "postgres" }); - - actualJob.Strategy.MatrixV2["dotnet-version"] - .Should().BeEquivalentTo(new List { "8.0.x", "10.0.100" }); - } - - [Fact] - public void ShouldAppendMultipleMatrixIncludeEntriesThroughPipeline() - { - // given - string inputJobName = "build"; - - var firstInclude = new Dictionary - { - ["provider"] = "sqlserver", - ["connection_string"] = GetRandomString() - }; - - var secondInclude = new Dictionary - { - ["provider"] = "postgres", - ["dotnet-version"] = "9.0.x", - ["connection_string"] = GetRandomString() - }; - - // when - var pipelineBuilder = this.gitHubPipelineBuilder - .AddJob(inputJobName, job => job - .AddMatrixInclude(firstInclude) - .AddMatrixInclude(secondInclude)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - - var actualIncludeList = - actualJob.Strategy.Include as List>; - - actualIncludeList.Should().NotBeNull(); - actualIncludeList.Should().HaveCount(2); - actualIncludeList.Should().ContainEquivalentOf(firstInclude); - actualIncludeList.Should().ContainEquivalentOf(secondInclude); - } - - [Fact] - public void ShouldAppendMatrixExcludeEntryThroughPipeline() - { - // given - string inputJobName = "build"; - - var excludeEntry = new Dictionary - { - ["provider"] = "sqlserver", - ["dotnet-version"] = "8.0.x" - }; - - // when - var pipelineBuilder = this.gitHubPipelineBuilder - .AddJob(inputJobName, job => job - .AddMatrix("provider", "sqlserver", "postgres") - .AddMatrix("dotnet-version", "8.0.x", "10.0.100") - .AddMatrixExclude(excludeEntry)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - - var actualExcludeList = - actualJob.Strategy.Exclude as List>; - - actualExcludeList.Should().NotBeNull(); - actualExcludeList.Should().ContainSingle(); - actualExcludeList.Should().ContainEquivalentOf(excludeEntry); - - actualJob.Strategy.MatrixV2["provider"] - .Should().BeEquivalentTo(new List { "sqlserver", "postgres" }); - } - - [Fact] - public void ShouldKeepIncludeAndExcludeIndependentThroughPipeline() - { - string inputJobName = "build"; - var includeEntry = new Dictionary { ["provider"] = "postgres" }; - var excludeEntry = new Dictionary { ["provider"] = "sqlserver" }; - - // when - var pipelineBuilder = this.gitHubPipelineBuilder - .AddJob(inputJobName, job => job - .AddMatrixInclude(includeEntry) - .AddMatrixExclude(excludeEntry)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - - var actualInclude = - actualJob.Strategy.Include; - - var actualExclude = - actualJob.Strategy.Exclude; - - actualInclude.Should().ContainSingle().Which.Should().BeEquivalentTo(includeEntry); - actualExclude.Should().ContainSingle().Which.Should().BeEquivalentTo(excludeEntry); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void ShouldSetFailFastThroughPipeline(bool inputFailFast) - { - // given - string inputJobName = "build"; - - // when - var pipelineBuilder = this.gitHubPipelineBuilder - .AddJob(inputJobName, job => job - .WithFailFast(inputFailFast)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - actualJob.Strategy.Should().NotBeNull(); - actualJob.Strategy.FailFast.Should().Be(inputFailFast); - } - - [Fact] - public void ShouldSetMaxParallelThroughPipeline() - { - // given - string inputJobName = "build"; - int inputMaxParallel = GetRandomNumber(); - - // when - var pipelineBuilder = this.gitHubPipelineBuilder - .AddJob(inputJobName, job => job - .WithMaxParallel(inputMaxParallel)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - actualJob.Strategy.Should().NotBeNull(); - actualJob.Strategy.MaxParallel.Should().Be(inputMaxParallel); - } - - [Fact] - public void ShouldAddServiceWithFullKeySetThroughPipeline() - { - string inputJobName = "build"; - - var inputService = new Service - { - Image = GetRandomString(), - - Credentials = new Credentials - { - Username = GetRandomString(), - Password = GetRandomString() - }, - - Environment = new Dictionary - { - [GetRandomString()] = GetRandomString() - }, - - Ports = new List { "5432:5432" }, - Volumes = new List { "pgdata:/var/lib/postgresql/data" }, - Options = GetRandomString() - }; - - // when - var pipelineBuilder = this.gitHubPipelineBuilder - .AddJob(inputJobName, job => job - .AddService("postgres", inputService)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - actualJob.Services.Should().ContainKey("postgres"); - actualJob.Services["postgres"].Should().BeEquivalentTo(inputService); - } - - [Fact] - public void ShouldOverwriteServiceWhenSameIdAddedTwiceThroughPipeline() - { - // given - string inputJobName = "build"; - var firstService = new Service { Image = GetRandomString() }; - var secondService = new Service { Image = GetRandomString() }; - - // when - var pipelineBuilder = this.gitHubPipelineBuilder - .AddJob(inputJobName, job => job - .AddService("postgres", firstService) - .AddService("postgres", secondService)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - actualJob.Services.Should().ContainSingle(); - actualJob.Services["postgres"].Should().BeEquivalentTo(secondService); - } } } From 46fa07d17bdf07b0b8dcc69f5a012cc19cf982c5 Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:35:13 +0100 Subject: [PATCH 31/33] CODERUB: Added V2 unit test --- .../GitHubPipelineBuilderV2Tests.Logic.cs | 375 ++++++++++++++++++ .../Builders/GitHubPipelineBuilderV2Tests.cs | 72 ++++ 2 files changed, 447 insertions(+) create mode 100644 AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.Logic.cs create mode 100644 AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.cs diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.Logic.cs new file mode 100644 index 0000000..30f1988 --- /dev/null +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.Logic.cs @@ -0,0 +1,375 @@ +// --------------------------------------------------------------------------- +// 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.Clients.Builders; +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 +{ + public partial class GitHubPipelineBuilderV2Tests + { + [Fact] + public void ShouldCreateNewPipeline() + { + // given..when + var builder = GitHubPipelineBuilderV2.CreateNewPipeline(); + + // then + builder.Should().NotBeNull(); + } + + [Fact] + public void ShouldSetPipelineName() + { + // given + string inputName = "My GitHub Pipeline"; + string expectedName = inputName; + + // when + var pipelineBuilder = GitHubPipelineBuilderV2.CreateNewPipeline() + .SetName(inputName); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + actualPipeline.Should().NotBeNull(); + actualPipeline.Name.Should().BeEquivalentTo(expectedName); + } + + [Fact] + public void ShouldAddPushTrigger() + { + // given + string[] inputBranches = { "main", "dev" }; + + // when + var pipelineBuilder = GitHubPipelineBuilderV2.CreateNewPipeline() + .OnPush(inputBranches); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + actualPipeline.OnEvents.Push.Should().NotBeNull(); + actualPipeline.OnEvents.Push.Branches.Should().BeEquivalentTo(inputBranches); + } + + [Fact] + public void ShouldAddPullRequestTrigger() + { + // given + string[] inputBranches = { "main", "feature/*" }; + + // when + var pipelineBuilder = GitHubPipelineBuilderV2.CreateNewPipeline() + .OnPullRequest(inputBranches); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + 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 = GitHubPipelineBuilderV2.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); + } + + [Fact] + public void ShouldSavePipelineToFile() + { + // given + string randomFileName = GetRandomFileName(); + string randomPipelineName = GetRandomString(); + + GithubPipelineV2 randomPipeline = + CreateRandomGithubPipelineV2(randomPipelineName); + + GithubPipelineV2 inputPipeline = randomPipeline; + string inputPath = randomFileName; + string inputPipelineName = randomPipelineName; + + this.aDotNetClientMock.Setup(client => + client.SerializeAndWriteToFile( + inputPipeline, + inputPath)) + .Verifiable(); + + this.gitHubPipelineBuilderV2.SetName(inputPipelineName); + + // when + this.gitHubPipelineBuilderV2.SaveToFile(inputPath); + + // then + this.aDotNetClientMock.Verify(client => + client.SerializeAndWriteToFile( + It.IsAny(), + It.IsAny()), + Times.Once); + + this.aDotNetClientMock.VerifyNoOtherCalls(); + } + + [Fact] + public void ShouldBuildMatrixWithMultipleAxesThroughPipeline() + { + // given + string inputJobName = "build"; + + // when + var pipelineBuilder = this.gitHubPipelineBuilderV2 + .AddJob(inputJobName, job => job + .AddMatrix("provider", "sqlserver", "postgres") + .AddMatrix("dotnet-version", "8.0.x", "10.0.100")); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Strategy.Should().NotBeNull(); + actualJob.Strategy.Matrix.Should().ContainKey("provider"); + actualJob.Strategy.Matrix.Should().ContainKey("dotnet-version"); + + actualJob.Strategy.Matrix["provider"] + .Should().BeEquivalentTo(new List { "sqlserver", "postgres" }); + + actualJob.Strategy.Matrix["dotnet-version"] + .Should().BeEquivalentTo(new List { "8.0.x", "10.0.100" }); + } + + [Fact] + public void ShouldAppendMultipleMatrixIncludeEntriesThroughPipeline() + { + // given + string inputJobName = "build"; + + var firstInclude = new Dictionary + { + ["provider"] = "sqlserver", + ["connection_string"] = GetRandomString() + }; + + var secondInclude = new Dictionary + { + ["provider"] = "postgres", + ["dotnet-version"] = "9.0.x", + ["connection_string"] = GetRandomString() + }; + + // when + var pipelineBuilder = this.gitHubPipelineBuilderV2 + .AddJob(inputJobName, job => job + .AddMatrixInclude(firstInclude) + .AddMatrixInclude(secondInclude)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + + var actualIncludeList = + actualJob.Strategy.Include as List>; + + actualIncludeList.Should().NotBeNull(); + actualIncludeList.Should().HaveCount(2); + actualIncludeList.Should().ContainEquivalentOf(firstInclude); + actualIncludeList.Should().ContainEquivalentOf(secondInclude); + } + + [Fact] + public void ShouldAppendMatrixExcludeEntryThroughPipeline() + { + // given + string inputJobName = "build"; + + var excludeEntry = new Dictionary + { + ["provider"] = "sqlserver", + ["dotnet-version"] = "8.0.x" + }; + + // when + var pipelineBuilder = this.gitHubPipelineBuilderV2 + .AddJob(inputJobName, job => job + .AddMatrix("provider", "sqlserver", "postgres") + .AddMatrix("dotnet-version", "8.0.x", "10.0.100") + .AddMatrixExclude(excludeEntry)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + + var actualExcludeList = + actualJob.Strategy.Exclude as List>; + + actualExcludeList.Should().NotBeNull(); + actualExcludeList.Should().ContainSingle(); + actualExcludeList.Should().ContainEquivalentOf(excludeEntry); + + actualJob.Strategy.Matrix["provider"] + .Should().BeEquivalentTo(new List { "sqlserver", "postgres" }); + } + + [Fact] + public void ShouldKeepIncludeAndExcludeIndependentThroughPipeline() + { + string inputJobName = "build"; + var includeEntry = new Dictionary { ["provider"] = "postgres" }; + var excludeEntry = new Dictionary { ["provider"] = "sqlserver" }; + + // when + var pipelineBuilder = this.gitHubPipelineBuilderV2 + .AddJob(inputJobName, job => job + .AddMatrixInclude(includeEntry) + .AddMatrixExclude(excludeEntry)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + + var actualInclude = + actualJob.Strategy.Include; + + var actualExclude = + actualJob.Strategy.Exclude; + + actualInclude.Should().ContainSingle().Which.Should().BeEquivalentTo(includeEntry); + actualExclude.Should().ContainSingle().Which.Should().BeEquivalentTo(excludeEntry); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ShouldSetFailFastThroughPipeline(bool inputFailFast) + { + // given + string inputJobName = "build"; + + // when + var pipelineBuilder = this.gitHubPipelineBuilderV2 + .AddJob(inputJobName, job => job + .WithFailFast(inputFailFast)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Strategy.Should().NotBeNull(); + actualJob.Strategy.FailFast.Should().Be(inputFailFast); + } + + [Fact] + public void ShouldSetMaxParallelThroughPipeline() + { + // given + string inputJobName = "build"; + int inputMaxParallel = GetRandomNumber(); + + // when + var pipelineBuilder = this.gitHubPipelineBuilderV2 + .AddJob(inputJobName, job => job + .WithMaxParallel(inputMaxParallel)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Strategy.Should().NotBeNull(); + actualJob.Strategy.MaxParallel.Should().Be(inputMaxParallel); + } + + [Fact] + public void ShouldAddServiceWithFullKeySetThroughPipeline() + { + string inputJobName = "build"; + + var inputService = new Service + { + Image = GetRandomString(), + + Credentials = new Credentials + { + Username = GetRandomString(), + Password = GetRandomString() + }, + + Environment = new Dictionary + { + [GetRandomString()] = GetRandomString() + }, + + Ports = new List { "5432:5432" }, + Volumes = new List { "pgdata:/var/lib/postgresql/data" }, + Options = GetRandomString() + }; + + // when + var pipelineBuilder = this.gitHubPipelineBuilderV2 + .AddJob(inputJobName, job => job + .AddService("postgres", inputService)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Services.Should().ContainKey("postgres"); + actualJob.Services["postgres"].Should().BeEquivalentTo(inputService); + } + + [Fact] + public void ShouldOverwriteServiceWhenSameIdAddedTwiceThroughPipeline() + { + // given + string inputJobName = "build"; + var firstService = new Service { Image = GetRandomString() }; + var secondService = new Service { Image = GetRandomString() }; + + // when + var pipelineBuilder = this.gitHubPipelineBuilderV2 + .AddJob(inputJobName, job => job + .AddService("postgres", firstService) + .AddService("postgres", secondService)); + + var actualPipeline = GetPipeline(pipelineBuilder); + + // then + var actualJob = actualPipeline.Jobs[inputJobName]; + actualJob.Services.Should().ContainSingle(); + actualJob.Services["postgres"].Should().BeEquivalentTo(secondService); + } + } +} diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.cs new file mode 100644 index 0000000..fed3fb6 --- /dev/null +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.cs @@ -0,0 +1,72 @@ +// --------------------------------------------------------------------------- +// 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 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 GitHubPipelineBuilderV2Tests + { + private readonly Mock aDotNetClientMock; + private readonly GitHubPipelineBuilderV2 gitHubPipelineBuilderV2; + + public GitHubPipelineBuilderV2Tests() + { + this.aDotNetClientMock = new Mock(); + + this.gitHubPipelineBuilderV2 = new GitHubPipelineBuilderV2( + aDotNetClient: aDotNetClientMock.Object); + } + + private static GithubPipelineV2 GetPipeline(GitHubPipelineBuilderV2 builder) + { + var privateField = typeof(GitHubPipelineBuilderV2) + .GetField( + name: "githubPipelineV2", + bindingAttr: System.Reflection.BindingFlags.NonPublic + | System.Reflection.BindingFlags.Instance); + + return (GithubPipelineV2)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 GithubPipelineV2 CreateRandomGithubPipelineV2(string name) => + CreateGithubPipelineV2Filler(name).Create(); + + private static GithubPipelineV2 CreateRandomGithubPipelineV2() => + CreateGithubPipelineV2Filler(name: GetRandomString()).Create(); + + private static Filler CreateGithubPipelineV2Filler(string name) + { + var filler = new Filler(); + + filler.Setup() + .OnProperty(p => p.EnvironmentVariables) + .Use(() => new Dictionary + { + { GetRandomString(), GetRandomString() }, + { GetRandomString(), GetRandomString() } + }) + .OnType().Use(() => GetRandomString()); + + return filler; + } + } +} From 4c5534e9ab90961f9ce07df18b02bc2c4afbcbcb Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Tue, 14 Jul 2026 14:36:42 +0100 Subject: [PATCH 32/33] CODERUB: Code cleanup --- .../Clients/Builders/GitHubPipelineBuilderTests.Logic.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs index eaeb134..65cd9bb 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderTests.Logic.cs @@ -4,7 +4,6 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- -using System.Collections.Generic; using ADotNet.Clients.Builders; using ADotNet.Models.Pipelines.GithubPipelines.DotNets; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; From f53bb6d0eebb421e075891d654a955848fd393de Mon Sep 17 00:00:00 2001 From: Ahmad Salim <54475330+SlimAhmad@users.noreply.github.com> Date: Sun, 26 Jul 2026 11:52:42 +0100 Subject: [PATCH 33/33] CODERUB: Refactor GitHub pipeline configuration to use model-based strategy and services --- ADotNet/Clients/Builders/JobBuilderV2.cs | 156 ------------ AdoNet.Tests.Console/Program.cs | 202 ++++++++++----- .../GitHubPipelineBuilderV2Tests.Logic.cs | 231 ------------------ 3 files changed, 143 insertions(+), 446 deletions(-) diff --git a/ADotNet/Clients/Builders/JobBuilderV2.cs b/ADotNet/Clients/Builders/JobBuilderV2.cs index c269d36..9007caa 100644 --- a/ADotNet/Clients/Builders/JobBuilderV2.cs +++ b/ADotNet/Clients/Builders/JobBuilderV2.cs @@ -161,119 +161,6 @@ public JobBuilderV2 AddTestStep(string name = "Test", string command = null) return this; } - /// - /// Adds a generic step to the job with a custom command. - /// - /// The name of the step. - /// The command to execute for this step. - /// The current instance of . - public JobBuilderV2 AddGenericStep(string name, string runCommand) - { - this.job.Steps.Add(new GithubTask - { - Name = name, - Run = runCommand - }); - - return this; - } - - /// - /// Specifies the jobs that this job depends on. - /// - /// The names of the jobs that this job depends on. - /// The current instance of . - public JobBuilderV2 DependsOn(params string[] jobNames) - { - this.job.Needs = jobNames; - - return this; - } - - /// - /// Sets a conditional expression that determines whether the job runs - /// - /// The condition for the step. - /// The current instance of . - public JobBuilderV2 WithCondition(string condition) - { - this.job.If = condition; - - return this; - } - - /// - /// Adds an axis variable (e.g. "provider": ["sqlserver", "postgres"]) to the job's matrix strategy. - /// - /// The name of the matrix axis variable. - /// The values for the matrix axis. - /// The current instance of . - public JobBuilderV2 AddMatrix( - string variable, - params string[] values) - { - this.job.Strategy ??= new StrategyV2(); - this.job.Strategy.Matrix ??= new Dictionary(); - this.job.Strategy.Matrix[variable] = new List(values); - - return this; - } - - /// - /// Adds a matrix "include" combination, adding a new configuration or extending an existing one. - /// - /// The key/value pairs describing the combination to include. - /// The current instance of . - public JobBuilderV2 AddMatrixInclude(Dictionary include) - { - this.job.Strategy ??= new StrategyV2(); - this.job.Strategy.Include ??= new List>(); - this.job.Strategy.Include.Add(include); - - return this; - } - - /// - /// Adds a matrix "exclude" combination, removing a matching configuration. - /// - /// The key/value pairs describing the combination to exclude. - /// The current instance of . - public JobBuilderV2 AddMatrixExclude(Dictionary exclude) - { - this.job.Strategy ??= new StrategyV2(); - this.job.Strategy.Exclude ??= new List>(); - this.job.Strategy.Exclude.Add(exclude); - - return this; - } - - /// - /// Sets whether the job's matrix strategy cancels all in-progress jobs if any matrix job fails. - /// GitHub Actions defaults to true; set this explicitly to override. - /// - /// Whether to fail fast. - /// The current instance of . - public JobBuilderV2 WithFailFast(bool failFast) - { - this.job.Strategy ??= new StrategyV2(); - this.job.Strategy.FailFast = failFast; - - return this; - } - - /// - /// Sets the maximum number of jobs that can run simultaneously from the matrix strategy. - /// - /// The maximum number of parallel jobs. - /// The current instance of . - public JobBuilderV2 WithMaxParallel(int maxParallel) - { - this.job.Strategy ??= new StrategyV2(); - this.job.Strategy.MaxParallel = maxParallel; - - return this; - } - /// /// Adds a generic run-based step, optionally with an id so later steps can reference its outputs. /// @@ -299,49 +186,6 @@ public JobBuilderV2 AddGenericStep( return this; } - /// - /// Adds a step to the job that uses a specific action (e.g. "actions/checkout@v3"), - /// optionally with an id, input parameters, and environment variables. - /// - /// The name of the step. - /// The GitHub Action to use. - /// The id of the step. - /// The input parameters for the action. - /// The environment variables for the step. - /// The current instance of . - public JobBuilderV2 AddActionStep( - string name, - string uses, - string id = null, - Dictionary with = null, - Dictionary environmentVariables = null) - { - this.job.Steps.Add(new GithubTask - { - Id = id, - Name = name, - Uses = uses, - With = with, - EnvironmentVariables = environmentVariables - }); - - return this; - } - - /// - /// Attaches a service container to the job. - /// - /// The service id, used as the key under the job's "services" map. - /// The service container definition. - /// The current instance of . - public JobBuilderV2 AddService(string id, Service service) - { - this.job.Services ??= new Dictionary(); - this.job.Services[id] = service; - - return this; - } - /// /// Builds and returns the configured job. /// diff --git a/AdoNet.Tests.Console/Program.cs b/AdoNet.Tests.Console/Program.cs index db40b29..36ffa96 100644 --- a/AdoNet.Tests.Console/Program.cs +++ b/AdoNet.Tests.Console/Program.cs @@ -221,71 +221,155 @@ static void Main(string[] args) .SaveToFile("github-pipelines-fluent.yaml"); - GitHubPipelineBuilderV2.CreateNewPipeline() - .SetName("test-strategy-services-surface") - .OnPush("main") - .OnPullRequest("main") - - .AddJob("full_surface", job => job - .WithName("Full Strategy + Services Surface") - .RunsOn(BuildMachines.UbuntuLatest) - .AddMatrix("provider", "sqlserver", "postgres") - .AddMatrix("dotnet-version", "8.0.x", "10.0.100") - .AddMatrixInclude(new() - { - ["provider"] = "sqlserver", - ["connection_string"] = - "Server=localhost;Database=TestDb;User Id=sa;" + - "Password=Your_password123!;TrustServerCertificate=True;" + - "MultipleActiveResultSets=true;Pooling=false" - }) - .AddMatrixInclude(new() - { - ["provider"] = "postgres", - ["connection_string"] = - "Host=localhost;Database=TestDb;Username=postgres;" + - "Password=postgres;Pooling=false" - }) - .AddMatrixInclude(new() + var githubPipelineV2 = new GithubPipelineV2 + { + Name = "test-strategy-services-surface", + + OnEvents = new Events + { + Push = new PushEvent { - ["provider"] = "postgres", - ["dotnet-version"] = "9.0.x", - ["connection_string"] = - "Host=localhost;Database=TestDb;Username=postgres;" + - "Password=postgres;Pooling=false" - }) - .AddMatrixExclude(new() + Branches = new[] { "main" } + }, + + PullRequest = new PullRequestEvent { - ["provider"] = "sqlserver", - ["dotnet-version"] = "8.0.x" - }) - .WithFailFast(false) - .WithMaxParallel(2) - .AddService("postgres", new Service + Branches = new[] { "main" } + } + }, + + Jobs = new Dictionary + { + ["full_surface"] = new JobV2 { - Image = "postgres:17", - Environment = new Dictionary + Name = "Full Strategy + Services Surface", + RunsOn = BuildMachines.UbuntuLatest, + + Strategy = new StrategyV2 { - ["POSTGRES_DB"] = "TestDb", - ["POSTGRES_USER"] = "postgres", - ["POSTGRES_PASSWORD"] = "postgres" + FailFast = false, + MaxParallel = 2, + + Matrix = new Dictionary + { + ["provider"] = new List + { + "sqlserver", + "postgres" + }, + + ["dotnet-version"] = new List + { + "8.0.x", + "10.0.100" + } + }, + + Include = new List> + { + new() + { + ["provider"] = "sqlserver", + ["connection_string"] = + "Server=localhost;Database=TestDb;User Id=sa;" + + "Password=Your_password123!;TrustServerCertificate=True;" + + "MultipleActiveResultSets=true;Pooling=false" + }, + + new() + { + ["provider"] = "postgres", + ["connection_string"] = + "Host=localhost;Database=TestDb;" + + "Username=postgres;Password=postgres;Pooling=false" + }, + + new() + { + ["provider"] = "postgres", + ["dotnet-version"] = "9.0.x", + ["connection_string"] = + "Host=localhost;Database=TestDb;" + + "Username=postgres;Password=postgres;Pooling=false" + } + }, + + Exclude = new List> + { + new() + { + ["provider"] = "sqlserver", + ["dotnet-version"] = "8.0.x" + } + } }, - Ports = new List { "5432:5432" }, - Options = - "--health-cmd pg_isready --health-interval 10s " + - "--health-timeout 5s --health-retries 5" - }) - .AddCheckoutStep("Check out") - .AddSetupDotNetStep("${{ matrix.dotnet-version }}") - .AddRestoreStep() - .AddBuildStep() - .AddGenericStep( - name: "Test", - runCommand: - "dotnet test Test.Core.Tests.Acceptance " + - "--no-build --verbosity normal")) - .SaveToFile("github-pipelines-strategy-services.yaml"); + Services = new Dictionary + { + ["postgres"] = new Service + { + Image = "postgres:17", + + Environment = new Dictionary + { + ["POSTGRES_DB"] = "TestDb", + ["POSTGRES_USER"] = "postgres", + ["POSTGRES_PASSWORD"] = "postgres" + }, + + Ports = new List + { + "5432:5432" + }, + + Options = + "--health-cmd pg_isready " + + "--health-interval 10s " + + "--health-timeout 5s " + + "--health-retries 5" + } + }, + + Steps = new List + { + new CheckoutTaskV5 + { + Name = "Check out" + }, + + new SetupDotNetTaskV5 + { + Name = "Setup .NET", + + With = new TargetDotNetVersionV5 + { + DotNetVersion = "${{ matrix.dotnet-version }}" + } + }, + + new RestoreTask + { + Name = "Restore" + }, + + new DotNetBuildTask + { + Name = "Build" + }, + + new GithubTask + { + Name = "Test", + Run = + "dotnet test Test.Core.Tests.Acceptance " + + "--no-build --verbosity normal" + } + } + } + } + }; + + adoClient.SerializeAndWriteToFile(githubPipeline, "github-pipelines-strategy-services.yaml"); } } } diff --git a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.Logic.cs b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.Logic.cs index 30f1988..2429ba1 100644 --- a/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.Logic.cs +++ b/AdoNet.Tests.Unit/Clients/Builders/GitHubPipelineBuilderV2Tests.Logic.cs @@ -4,7 +4,6 @@ // See License.txt in the project root for license information. // --------------------------------------------------------------------------- -using System.Collections.Generic; using ADotNet.Clients.Builders; using ADotNet.Models.Pipelines.GithubPipelines.DotNets; using ADotNet.Models.Pipelines.GithubPipelines.DotNets.Tasks; @@ -141,235 +140,5 @@ public void ShouldSavePipelineToFile() this.aDotNetClientMock.VerifyNoOtherCalls(); } - - [Fact] - public void ShouldBuildMatrixWithMultipleAxesThroughPipeline() - { - // given - string inputJobName = "build"; - - // when - var pipelineBuilder = this.gitHubPipelineBuilderV2 - .AddJob(inputJobName, job => job - .AddMatrix("provider", "sqlserver", "postgres") - .AddMatrix("dotnet-version", "8.0.x", "10.0.100")); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - actualJob.Strategy.Should().NotBeNull(); - actualJob.Strategy.Matrix.Should().ContainKey("provider"); - actualJob.Strategy.Matrix.Should().ContainKey("dotnet-version"); - - actualJob.Strategy.Matrix["provider"] - .Should().BeEquivalentTo(new List { "sqlserver", "postgres" }); - - actualJob.Strategy.Matrix["dotnet-version"] - .Should().BeEquivalentTo(new List { "8.0.x", "10.0.100" }); - } - - [Fact] - public void ShouldAppendMultipleMatrixIncludeEntriesThroughPipeline() - { - // given - string inputJobName = "build"; - - var firstInclude = new Dictionary - { - ["provider"] = "sqlserver", - ["connection_string"] = GetRandomString() - }; - - var secondInclude = new Dictionary - { - ["provider"] = "postgres", - ["dotnet-version"] = "9.0.x", - ["connection_string"] = GetRandomString() - }; - - // when - var pipelineBuilder = this.gitHubPipelineBuilderV2 - .AddJob(inputJobName, job => job - .AddMatrixInclude(firstInclude) - .AddMatrixInclude(secondInclude)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - - var actualIncludeList = - actualJob.Strategy.Include as List>; - - actualIncludeList.Should().NotBeNull(); - actualIncludeList.Should().HaveCount(2); - actualIncludeList.Should().ContainEquivalentOf(firstInclude); - actualIncludeList.Should().ContainEquivalentOf(secondInclude); - } - - [Fact] - public void ShouldAppendMatrixExcludeEntryThroughPipeline() - { - // given - string inputJobName = "build"; - - var excludeEntry = new Dictionary - { - ["provider"] = "sqlserver", - ["dotnet-version"] = "8.0.x" - }; - - // when - var pipelineBuilder = this.gitHubPipelineBuilderV2 - .AddJob(inputJobName, job => job - .AddMatrix("provider", "sqlserver", "postgres") - .AddMatrix("dotnet-version", "8.0.x", "10.0.100") - .AddMatrixExclude(excludeEntry)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - - var actualExcludeList = - actualJob.Strategy.Exclude as List>; - - actualExcludeList.Should().NotBeNull(); - actualExcludeList.Should().ContainSingle(); - actualExcludeList.Should().ContainEquivalentOf(excludeEntry); - - actualJob.Strategy.Matrix["provider"] - .Should().BeEquivalentTo(new List { "sqlserver", "postgres" }); - } - - [Fact] - public void ShouldKeepIncludeAndExcludeIndependentThroughPipeline() - { - string inputJobName = "build"; - var includeEntry = new Dictionary { ["provider"] = "postgres" }; - var excludeEntry = new Dictionary { ["provider"] = "sqlserver" }; - - // when - var pipelineBuilder = this.gitHubPipelineBuilderV2 - .AddJob(inputJobName, job => job - .AddMatrixInclude(includeEntry) - .AddMatrixExclude(excludeEntry)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - - var actualInclude = - actualJob.Strategy.Include; - - var actualExclude = - actualJob.Strategy.Exclude; - - actualInclude.Should().ContainSingle().Which.Should().BeEquivalentTo(includeEntry); - actualExclude.Should().ContainSingle().Which.Should().BeEquivalentTo(excludeEntry); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void ShouldSetFailFastThroughPipeline(bool inputFailFast) - { - // given - string inputJobName = "build"; - - // when - var pipelineBuilder = this.gitHubPipelineBuilderV2 - .AddJob(inputJobName, job => job - .WithFailFast(inputFailFast)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - actualJob.Strategy.Should().NotBeNull(); - actualJob.Strategy.FailFast.Should().Be(inputFailFast); - } - - [Fact] - public void ShouldSetMaxParallelThroughPipeline() - { - // given - string inputJobName = "build"; - int inputMaxParallel = GetRandomNumber(); - - // when - var pipelineBuilder = this.gitHubPipelineBuilderV2 - .AddJob(inputJobName, job => job - .WithMaxParallel(inputMaxParallel)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - actualJob.Strategy.Should().NotBeNull(); - actualJob.Strategy.MaxParallel.Should().Be(inputMaxParallel); - } - - [Fact] - public void ShouldAddServiceWithFullKeySetThroughPipeline() - { - string inputJobName = "build"; - - var inputService = new Service - { - Image = GetRandomString(), - - Credentials = new Credentials - { - Username = GetRandomString(), - Password = GetRandomString() - }, - - Environment = new Dictionary - { - [GetRandomString()] = GetRandomString() - }, - - Ports = new List { "5432:5432" }, - Volumes = new List { "pgdata:/var/lib/postgresql/data" }, - Options = GetRandomString() - }; - - // when - var pipelineBuilder = this.gitHubPipelineBuilderV2 - .AddJob(inputJobName, job => job - .AddService("postgres", inputService)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - actualJob.Services.Should().ContainKey("postgres"); - actualJob.Services["postgres"].Should().BeEquivalentTo(inputService); - } - - [Fact] - public void ShouldOverwriteServiceWhenSameIdAddedTwiceThroughPipeline() - { - // given - string inputJobName = "build"; - var firstService = new Service { Image = GetRandomString() }; - var secondService = new Service { Image = GetRandomString() }; - - // when - var pipelineBuilder = this.gitHubPipelineBuilderV2 - .AddJob(inputJobName, job => job - .AddService("postgres", firstService) - .AddService("postgres", secondService)); - - var actualPipeline = GetPipeline(pipelineBuilder); - - // then - var actualJob = actualPipeline.Jobs[inputJobName]; - actualJob.Services.Should().ContainSingle(); - actualJob.Services["postgres"].Should().BeEquivalentTo(secondService); - } } }