Giter Club home page Giter Club logo

adotnet's Introduction

ADotNet

ADotNet

.NET Nuget Nuget The Standard The Standard - COMPLIANT The Standard Community

ADotNet (ADO Dot Net)

ADotNet is a.NET library that enables software engineers on the .NET platform to develop AzureDevOps pipelines and Git Actions in C#.

Introduction

There's an issue today with developing Azure DevOps pipelines and Git Actions with YAML. The technology/language can be challenging to learn and predict the available options for orchestrating build steps. ADotNet presents a solution to pipeline tasks as C# models, predefined, with all the options available to orchestrate a pipeline without searching for the available options on the documentation websites.

Standard-Compliance

This library was built according to The Standard. The library follows engineering principles, patterns and tooling as recommended by The Standard.

This library is also a community effort which involved many nights of pair-programming, test-driven development and in-depth exploration research and design discussions.

Standard-Promise

The most important fulfillment aspect in a Standard complaint system is aimed towards contributing to people, its evolution, and principles. An organization that systematically honors an environment of learning, training, and sharing knowledge is an organization that learns from the past, makes calculated risks for the future, and brings everyone within it up to speed on the current state of things as honestly, rapidly, and efficiently as possible.

We believe that everyone has the right to privacy, and will never do anything that could violate that right. We are committed to writing ethical and responsible software, and will always strive to use our skills, coding, and systems for the good. We believe that these beliefs will help to ensure that our software(s) are safe and secure and that it will never be used to harm or collect personal data for malicious purposes.

The Standard Community as a promise to you is in upholding these values.

How It Works for AzureDevOps

Here's how this library works. Let's assume you want to write a task in your pipeline that restores packages for your ASP.NET Core project. Today, engineers write the following command in YAML:

- task: DotNetCoreCLI@2
  displayName: 'Restore'
  inputs:
    command: 'restore'
    feedsToUse: 'select'

The problem with the above YAML code is that it's not that easy to remember. Even while staring at it, I just can't seem to remember DotNetCoreCLI@2 and what does this mean to someone who is a full-stack engineer trying to get off the ground as fast as possible? Here's how the very same code above would look like in ADotNet:

  new DotNetExecutionTask
  {
      DisplayName = "Restore",

      Inputs = new DotNetExecutionTasksInputs
      {
          Command = Command.restore,
          FeedsToUse = Feeds.select
      }
  }

The options here are available with the power of strongly typed options and Enums. You don't have to think about what needs to go there. The syntax is already directing you towards the options you need to get going with building your pipeline.

How It Works for Git Actions

Here's how this library works. Let's assume you want to write a task in your pipeline that uses a particular version for your ASP.NET Core project. Today, engineers write the following command in YAML:

- name: Setup .Net
    uses: actions/setup-dotnet@v1
    with:
      dotnet-version: 6.0.100-rc.1.21463.6
      include-prerelease: true

The problem with the above YAML code is that it's not that easy to remember. Even while I'm staring at it, I just can't seem to remember actions/setup-dotnet@v1 and what does this mean to someone who is a full-stack engineer trying to get off the ground as soon as possible? Here's how the very same code above would look like in ADotNet:

  new SetupDotNetTaskV1
  {
      Name = "Setup .Net",

      TargetDotNetVersion = new TargetDotNetVersion
      {
          DotNetVersion = "6.0.100-rc.1.21463.6",
          IncludePrerelease = true
      }
  }

The options here are available with the power of strongly typed options and Enums. You don't have to think about what needs to go there. It's already directing you towards the options you need to get going with building your pipeline.

Dependencies & Kudos

This library relies heavily on YamlDotNet which is an amazing .NET library developed by Antoine Aubry along with so many other amazing contributors who made C# to YAML possible.

The library also leverages native .NET System.IO.File functionality to write files to a destination of the consumer's choosing.

The Architecture

The library's architecture follows The Standard. Breaking it's capabilities into brokers, services and clients. Here's a low-level architecture view of how it works:

The abstraction of the dependencies should allow a future expansion and pluggability for any other C# to YAML components easily.

Real-Life Example for AzureDevOps

Here's something I'm using in my open source OtripleS project which is built in ASP.NET Core 6.0:

  var adoClient = new ADotNetClient();

  var aspNetPipeline = new AspNetPipeline
  {
      TriggeringBranches = new List<string>
      {
          "master"
      },

      VirtualMachinesPool = new VirtualMachinesPool
      {
          VirtualMachineImage = VirtualMachineImages.Windows2019
      },

      ConfigurationVariables = new ConfigurationVariables
      {
          BuildConfiguration = BuildConfiguration.Release
      },

      Tasks = new List<BuildTask>
      {
          new UseDotNetTask
          {
              DisplayName = "Use DotNet 6.0",

              Inputs = new UseDotNetTasksInputs
              {
                  Version = "6.0.100-preview.3.21202.5",
                  IncludePreviewVersions = true,
                  PackageType = PackageType.sdk
              }
          },

          new DotNetExecutionTask
          {
              DisplayName = "Restore",

              Inputs = new DotNetExecutionTasksInputs
              {
                  Command = Command.restore,
                  FeedsToUse = Feeds.select
              }
          },

          new DotNetExecutionTask
          {
              DisplayName = "Build",

              Inputs = new DotNetExecutionTasksInputs
              {
                  Command = Command.build,
              }
          },

          new DotNetExecutionTask
          {
              DisplayName = "Test",

              Inputs = new DotNetExecutionTasksInputs
              {
                  Command = Command.test,
                  Projects = "**/*Unit*.csproj"
              }
          },

          new DotNetExecutionTask
          {
              DisplayName = "Publish",

              Inputs = new DotNetExecutionTasksInputs
              {
                  Command = Command.publish,
                  PublishWebProjects = true
              }
          }
      }
  };

  adoClient.SerializeAndWriteToFile(aspNetPipeline, "../../azure-pipelines.yaml");

And here's the YAML output of this code:

trigger:
- master
pool:
  vmImage: 'ubuntu-latest'
variables:
  buildConfiguration: 'Release'
steps:
- task: UseDotNet@2
  displayName: 'Use DotNet 6.0'
  inputs:
    packageType: 'sdk'
    version: '6.0.100-preview.3.21202.5'
    includePreviewVersions: true
- task: DotNetCoreCLI@2
  displayName: 'Restore'
  inputs:
    command: 'restore'
    feedsToUse: 'select'
- task: DotNetCoreCLI@2
  displayName: 'Build'
  inputs:
    command: 'build'
- task: DotNetCoreCLI@2
  displayName: 'Test'
  inputs:
    command: 'test'
    projects: '**/*Unit*.csproj'
- task: DotNetCoreCLI@2
  displayName: 'Publish'
  inputs:
    command: 'publish'
    publishWebProjects: true

And finally, here's the result:

image

Real-Life Example for Git Actions

Here's something I'm using in my open source OtripleS project which is built in ASP.NET Core 6.0:

  var aDotNetClient = new ADotNetClient();

  var githubPipeline = new GithubPipeline
  {
      Name = "Github",

      OnEvents = new Events
      {
          Push = new PushEvent
          {
              Branches = new string[] { "master" }
          },

          PullRequest = new PullRequestEvent
          {
              Branches = new string[] { "master" }
          }
      },

      Jobs = new Dictionary<string, Job>
      {
          {
              "build",
              new Job
              {
                  RunsOn = BuildMachines.Windows2019,

                  Steps = new List<GithubTask>
                  {
                      new CheckoutTaskV2
                      {
                          Name = "Check out"
                      },

                      new SetupDotNetTaskV1
                      {
                          Name = "Setup .Net",

                          TargetDotNetVersion = new TargetDotNetVersion
                          {
                              DotNetVersion = "6.0.100-rc.1.21463.6",
                              IncludePrerelease = true
                          }
                      },

                      new RestoreTask
                      {
                          Name = "Restore"
                      },

                      new DotNetBuildTask
                      {
                          Name = "Build"
                      },

                      new TestTask
                      {
                          Name = "Test"
                      }
                  }
              }
          }
      }
  };

  string buildScriptPath = "../../../../.github/workflows/dotnet.yml";
  string directoryPath = Path.GetDirectoryName(buildScriptPath);

  if (!Directory.Exists(directoryPath))
  {
      Directory.CreateDirectory(directoryPath);
  }

  aDotNetClient.SerializeAndWriteToFile(githubPipeline, path: buildScriptPath);

And here's the YAML output of this code:

name: Github
on:
  push:
    branches:
    - master
  pull_request:
    branches:
    - master
jobs:
  build:
    runs-on: windows-2019
    steps:
    - name: Check out
      uses: actions/checkout@v2
    - name: Setup .Net
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 6.0.100-rc.1.21463.6
        include-prerelease: true
    - name: Restore
      run: dotnet restore
    - name: Build
      run: dotnet build --no-restore
    - name: Test
      run: dotnet test --no-build --verbosity normal

And finally, here's the result:

image

Some Odd Decisions

I have intentionally limited some of the capabilities in this library to ensure any contributions go to this repository so everyone can benefit from the updates. For instance, I could've easily made selecting a virtual machine as a string input to allow for anyone to pass in whatever vm they need. But the problem with that is for those who will need the same thing and have to do the same research to find the right VM for their build.

I'm intentionally making my library less usable to ensure this level of hive mindset is reflected in our changes in here.

If you have any suggestions, comments or questions, please feel free to contact me on:

Twitter

LinkedIn

E-Mail

Important Notice

Special thanks to all the contributors that make this project a success. A special thanks to Mr. Hassan Habib and Mr. Christo du Toit for their dedicated contribution.

adotnet's People

Contributors

aosunlana avatar catalin-andronie avatar cjdutoit avatar dlandi avatar glhays avatar hassanhabib avatar imjane5 avatar sacheinbaala avatar shrihumrudha avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

adotnet's Issues

FOUNDATIONS: checkout@v3 and setup-dotnet@v3 tasks

Upgrade tasks to V3 to get rid of build warnings

Node.js 12 actions are deprecated. 
Please update the following actions to use Node.js 16: actions/checkout@v2, actions/setup-dotnet@v1. 
For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/.

MINOR FIX: IS_RELEASE_CANDIDATE

To verify that IS_RELEASE_CANDIDATE is still true for all conditions.

From @glhays:
I found in some of my testing that these lines
"startsWith(github.event.pull_request.title, 'RELEASES:') &&\r"
"contains(github.event.pull_request.labels.*.name, 'RELEASES')",
can fail especially the contains when the label is RELEASES and the expression contains does not evaluate to true, but if you change it to RELEASE it does as in the EnviromentVariables expression for the RELEASE CANDIDATE check.
When I removed the created release tag and reran the failed action it went through without a RELEASE label.

RELEASES: Add Git Tag on Merge to Main

The Ask

Create a job that will add a Git tag RELEASE to only when the pull request is merged into the main branch.

How will this be used

If a tag can be added on merge, then the tag can be used on other workflow runs i.e. if a build action run on main and it detects a tag RELEASE then we can automatically deploy a nuget package if build & test is a success.

Adding a PublishTask

Hello,

could you please also add a PublishTask ? Also to deploy a .net core app to a preferred IIS.

Something like

 public class PublishTask : GithubTask
    {
        [YamlMember(Order = 1)]
        public string Run = "dotnet publish .....";
    }

RELEASES: Publish Nuget Package

The Ask

Amend Program.cs in the ADotNet.Infrastructure.Build project to show how the publish job could be used to publish a NuGet package.

Once changes is made, run the ADotNet.Infrastructure.Build projects so that the new dotnet.yml can be check in with the changes.

RELEASES: Add Functionality That Would Check For Release Candidate

The Ask

  1. Extend ADotNet to add a top level environment variable IS_RELEASE_CANDIDATE with a value of either TRUE or FALSE

    The value for IS_RELEASE_CANDIDATE will be TRUE if the GitHub action detects that either of these are TRUE:

    • github.event_name == 'pull_request' with a title starting with RELEASES: and an associated label RELEASES
    • github.event_name == 'push' && startsWith(github.event.head_commit.message, 'RELEASES:') && github.ref_type == 'RELEASE'
  2. Enable build workflow to run on RELEASE tags.

How will this be used / Why is this important?

The IS_RELEASE_CANDIDATE environment variable can be used in test project to create a dynamic trait filter that will either RUN or SKIP tests based on the TRUE / FALSE value.

Matrix feature

@hassanhabib FYI, as mentioned regarding the fun issue.

I have made a feature for adding Matrix for Github action.
image

But there are issue with YamlDotNet serlizer, it generate single quote for brackets:
image

I created a feature request at YamlDotNet:
aaubry/YamlDotNet#676

I will wait a few day to see what will happen with it, but mean while I am creating a working around and will make a PR for Matrix solution for ADotNet.

if there are suggestion mean while let me know.

CONFIG: Target Version

This library now targets .Net 7.0. The issue with this is compatibility with older versions when pipeline building. Normally this is fine but in the case of SDK pipeline's the developer may want to target a LTS version currently 6.0.

The library has a dependency of YamlDotNet & Xeption

I would recommend after looking at the dependencies of these library's targeting "netstandard2.0;netstandard2.1"

This will allow the solution the library is consumed by to support any version compatible with netstandard2.0 & netstandard2.1

An example of the issue is in the current Standard.AI.OpenAI repository, we cannot target netstandard2.1 for integration with a wider range of clients, as the build pipeline fails if we are not targeting .Net 7.0 because of this restriction.

INFRA: Add Images Folder Future Nuget Pkgs Fixes

Adds an assets folder named images with a set of supporting pre done images, icons, master psd and git header logo for future nuget package uploads and or automation builds.

Readme updated with nuget badges appropriately for downloads and community.

Once committed/merged the current readme.md header html block for rendering the git_logo file can be replaced with the markdown syntax.
Sample, may need be redefined post: ![}(https://raw.githubusercontent.com/hassanhabib/ADotNet/master/git_logo.png)

for very big error :(

            Jobs = new Jobs
            {
                Build = new BuildJob()
                {
                    RunsOn = BuildMachines.Windows2022,

                    Steps = new List<GithubTask>()
            {
                new CheckoutTaskV2()
                {
                    Name = "Checking Out Code"
                },

                new SetupDotNetTaskV1()
                {
                    Name = "Setting up .NET",
                    TargetDotNetVersion = new TargetDotNetVersion()
                    {
                       DotNetVersion = "8.0.101"
                    }

                },

                new RestoreTask()
                {
                    Name = "Restoring Nuget Packages"
                },

                new DotNetBuildTask()
                {
                    Name = "Build Project"
                },

                new TestTask()
                {
                    Name = "Running Tests"
                }

            }
                }
            }

i mean this is not worked, because you can create this class is obsolutly,

EXPOSURES: Add support for specifying the condition which can prevent a task from running

For example, pending #43

new NuGetPushTaskBuilder()
    .WithName("Publish")
    .If("${{ github.event.inputs.publish == 'true' }}") // something like this should be added
    .WithSearchPath(@"**\*.nupkg")
    .WithApiKey("${{ secrets.NUGET_API_KEY }}")
    .WithDestination("https://api.nuget.org/v3/index.json")
    .Build()

should generate the following YAML:

- name: Publish
   if: ${{ github.event.inputs.publish == 'true' }}
   run: dotnet nuget push "**\*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source "https://api.nuget.org/v3/index.json"

I'm not sure if it should be If, WithIf, or maybe something else.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.