Time is moving fast and a lot of things happing within the Software Development and DevOps world. You have properly heard a lot about Pipeline As Code (PaC). IMO the idea behind Pipeline as code is to ensure that we can at any time recreate and redeploy our environment in the same way so that we do not forget steps or have a concern about how to re-do it again.
There are of course different ways and frameworks for PaC, like Microsoft Bicep, Terraform, and Pulumi. You can of course also do it without having a specific framework, in Azure you can make PaC using Arm Template, PowerShell, etc.
In this article, I am going to build a very simple YAML workflow for GitHub actions for continuous integration that I already use in my open-source CryptoNet project. And I will later convert it to PaC as C# code using ADotNet a new and lightweight open-source library developed by Hassan Rezk Habib.
Let’s go for it.
My requirement for my continuous integration workflow file was as described below:
- To trigger actions on main and feature branches but exclude feature branches start with ci.
- To trigger actions on a pull request to the main branch.
- To build and test my code on 3 different platforms, windows, ubuntu, and mac os.
- To time out after 15 min if things fail or did not go well.
- To build and test my code using net 6.
With all that mentioned requirements, I come up with the following YAML file solution:
on:
# following triggers described in 1 and 2 in requirement
push:
branches:
- main
- "feature/*"
- "!feature/ci*"
paths-ignore:
- "**/README.md"
pull_request:
branches:
- main
jobs:
build:
# following matrix build described in 3 in requirement
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
# following timeout described in 4 in requirement
timeout-minutes: 15
# following steps refer to 5 in requirement
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
- name: Build
run: dotnet build --configuration Release
- name: Test
run: dotnet test --configuration Release --no-build
Now, if I need to create a C# code that can generate the above YAML file, I have to use ADotNet open-source library. I create a console project and add ADotNet NuGet to it, calling it CryptoNetPaC. In my Program class I add the following methods:
public static void CiYamlGenerator(string workflowName = "ci.yaml")
{
var adoClient = new ADotNetClient();
var aspNetPipeline = new GithubPipeline()
{
Name = ".NET",
OnEvents = new Events()
{
Push = new PushEvent()
{
Branches = new[]
{
"main",
"feature/*",
"!feature/ci*"
}
},
PullRequest = new PullRequestEvent()
{
Branches = new[]
{
"main"
}
}
},
Jobs = new Jobs()
{
Build = new BuildJob()
{
RunsOn = "ubuntu-latest",
TimeoutInMinutes = 15,
Steps = new List<GithubTask>()
{
new CheckoutTaskV2()
{
Name = "Checkout",
Uses = "actions/checkout@v2"
},
new SetupDotNetTaskV1()
{
Name = "Setup .NET",
Uses = "actions/setup-dotnet@v1",
TargetDotNetVersion = new TargetDotNetVersion()
{
DotNetVersion = "6.0.x"
}
},
new DotNetBuildTask()
{
Name = "Build",
Run = "dotnet build --configuration Release"
},
new TestTask()
{
Name = "Test",
Run = "dotnet test --configuration Release --no-build"
}
}
}
}
};
CreateWorkFlowFile(adoClient, aspNetPipeline, workflowName);
}
private static void CreateWorkFlowFile(ADotNetClient adoClient, GithubPipeline githubPipeline, string workflowName)
{
var solutionRoot = Directory.GetParent(Directory.GetCurrentDirectory())?.Parent?.Parent?.Parent?.FullName;
var workflowPath = $"{solutionRoot}\\.github\\workflows";
string workflowFile = Path.Combine(workflowPath, workflowName);
adoClient.SerializeAndWriteToFile(githubPipeline, workflowFile);
}
I followed the same structure in my YAML file for creating a C# PaC, that can automatically create the same pipeline. This gives me flexibility for adding some dynamically generated variable names and an easy way to maintain my pipeline using C# syntax for those developers that might have issues with YAML 😜.
Now when I run this code, it will create ci.yaml under .github/workflows folder.
I have also created CreateWorkFlowFile a method so I can reuse it for creating other YAML files.
That is it for this time 😃.
Conclusion
As you can see it was straightforward for me to create a YAML workflow pipeline as PaC in C# for GitHub using the ADotNet library. As you can see in my YAML file, there are keys like paths-ignore and matrix os, they are not supported yet in ADotNet but as time goes a lot of updates add to it. For example, I asked for the timeout-minutes feature, and it took 2 days and it was implemented. With the same library, you can also use it to create an Azure DevOps pipeline as well. You can of course also use a framework or combine it with other solutions as well. As it is a DevOps solution, there are no limits or rules on how to do it as long as it is well documented and delivers the results for your business. I will cover other ways of PaC in the future. Keep tuned.
Impressive blog with lovely information. Really a very useful article for us thanks for sharing such a wonderful blog.