Announcing Petabridge.Templates 2.0 - Professional Akka.NET Application and Library Templates

Ready-made Akka.NET application and library dotnet new templates for creating professional-quality projects!

We’ve prepared a set of three professional-grade dotnet new templates (Petabridge.Templates) for use with your projects - these will help you get started right away with Akka.NET or developing your own NuGet libraries that you wish to distribute. We’ve built and maintained these for years in order to create our own internal and external projects here at Petabridge.

But first, I should probably tell you a personal story, about how I improved the professionalism on my projects with build automation. Before I had the opportunity to work on a build system, I have always known the advantage of them and envied projects that had them.
On Twitter I read tweets from different .NET developers about creating a powerful build automations; I felt like it was a rocket science and secretly wished to be doing something like that. I was stuck, I needed a boost to move me out of that state.

For instance, my first ever NuGet package was released from my machine using NuGet CLI and the unit tests were executed manually, locally. For an open source project, you want to be very transparent and one way to do that is to have a build automation setup that everyone can see!

In late 2020, I was given an opportunity to work on Akka.NET’s build system; and, “Ah, may the force be with me”, I exclaimed! That was the opportunity I needed to get into the world of build automation - that was the force I needed! That force has remained with me ever since!

Maybe I “Was” You!

When it comes to creating professional grade software, you may belong to the category of developers who know what it takes but aren’t sure about the best practices or how to learn them. You want to write unit tests, add documentation, automate builds, but need an example to work from - Petabridge.Templates, which we’re releasing 2.0 of today, is that example.

The goal of Petabridge.Templates is to help you get started with a “best practices” .NET project structures quickly.

To install these templates, you’ll need to use the dotnet command line:

dotnet new -i "Petabridge.Templates::*"

This will make all of the templates included in this package (read on) available as options via the dotnet new command.

What Is Petabridge.Templates?

.NET Core brought about some dramatic tooling changes to the typical .NET experience with heavy emphasis now placed on the dotnet command line. .NET Core was created to be cross-platform and the command line is a great fit for this purpose. dotnet new is part of dotnet CLI - the entry point to all of .NET core goodness, which is mainly used to create projects driven from existing templates like console (console templates provide the ability to create a console application from the dotnet new command).

Petabridge.Templates is a collection of open source dotnet new templates maintained by Petabridge - and they’re designed for use by Petabridge’s own teams and end-users to create professional grade libraries, Akka.Cluster applications, and Akka.Cluster + ASP.NET integrations:

  • pb-lib - a professional .NET 6 project setup including build scripts, documentation, unit tests, and performance tests for a class library project.
  • pb-akka-cluster - creates a headless .NET 6 service that includes full Akka.NET clustering support, and Docker support.
  • pb-akka-web - does the same as pb-akka-cluster, but hosts Akka.NET inside an ASP.NET Core 6 simple web application.

You can read more about how these templates work by clicking on “source link” and reading the README.md for each individual project.

To use one of these templates, you can use the dotnet new command followed by the name or short-name.

dotnet new pb-lib [-n name] [other arguments]

So for instance, if I execute dotnet new pb-lib -n "MyProject" the following output will appear in my working directory:

D:\MyProject\.nuke
D:\MyProject\build.cmd
D:\MyProject\build.ps1
D:\MyProject\build.sh
D:\MyProject\MyProject.sln
D:\MyProject\README.md
D:\MyProject\CHANGELOG.md
D:\MyProject\gitversion.yml
D:\MyProject\build
D:\MyProject\build\_build.csproj
D:\MyProject\build\Build.CI.GitHubActions.cs
D:\MyProject\build\Build.cs
D:\MyProject\build\Configuration.cs
D:\MyProject\build\Directory.Build.props
D:\MyProject\build\Directory.Build.targets
D:\MyProject\docs
D:\MyProject\docs\api
D:\MyProject\docs\articles
D:\MyProject\docs\docfx.json
D:\MyProject\docs\images
D:\MyProject\docs\index.md
D:\MyProject\docs\toc.yml
D:\MyProject\docs\api\index.md
D:\MyProject\docs\articles\index.md
D:\MyProject\docs\articles\toc.yml
D:\MyProject\docs\images\icon.png
D:\MyProject\src
D:\MyProject\src\Directory.Build.props
D:\MyProject\src\MyProject
D:\MyProject\src\MyProject.Tests
D:\MyProject\src\MyProject.Tests.Performance
D:\MyProject\src\MyProject\Class1.cs
D:\MyProject\src\MyProject\MyProject.csproj
D:\MyProject\src\MyProject.Tests\MyProject.Tests.csproj
D:\MyProject\src\MyProject.Tests\UnitTest1.cs
D:\MyProject\src\MyProject.Tests.Performance\MyProject.Tests.Performance.csproj
D:\MyProject\src\MyProject.Tests.Performance\UnitTest1.cs

I’ll have a new .NET 6 library project complete with a cross-platform build system, documentation, unit tests, performance tests, and a README explaining how all of the above work ready to go from day one.

What’s Included?

Each templates within Petabridge.Templates has a build automation setup with it for GitHub Actions - and the templates have the following benefits:

  • Robust .NET Solution Hygiene - all dependency versions are synchronized across all projects using Directory.Build.props, as are .NET runtime versions, version data, and project metadata.
  • Easy Version Management - to bump the version on a NuGet package or Docker image, simply update the RELEASE_NOTES.md with your new version number (SemVer is supported) and run the appropriate build action either locally or on GitHub Actions.
  • Testing - each template has a standardized convention for discovering and running xUnit test projects.
  • Documentation: -you can generate DocFx documentation in a single command.
  • Benchmarks - each template has a performance project for writing and executing performance tests.
  • NuGet Publication - for the pb-lib template the build script has a PublishNuget with which NuGet packages can be built and published.
  • Code Signing - for the pb-lib template only, we have built-in support for using SignService to Authenticode sign NuGet packages with a certificate prior to NuGet publication. This is an optional step in the PublishNuGet build step.
  • Docker Image Creation - for the pb-akka-cluster and pb-akka-web templates we support automatically discovering Dockerfile instances and dotnet publish-ing the binaries for your applications and then executing the pre-baked build instructions inside the Dockerfile. Uses the version data from the RELEASE_NOTES.md.
  • Shovel-Ready Akka.NET - the pb-akka-cluster and pb-akka-web templates are built with Akka.NET hosting best practices from the start, so no guesswork needed to get up and running.

The previous version, 1.2.0, and earlier build automation was built on FAKE. In version 2, we rewrote the build automation from scratch, replacing FAKE with NUKE. Let me have a good laugh before saying this😂: for those that like tech wars, in this case FAKE vs NUKE, 14 reasons for adopting Nuke as your next build automation tool, will be a good use of your time!

When we decided to adopt NUKE, it was for two reasons and two reasons only (Nuke Build support). The sum, of the two reasons, is to greatly improve developers’ experience:

  • The cross-platform build automation solution for .NET with C# DSL: What this mean is C# developer can start automating builds almost immediately. With FAKE, aside learning how to use it, there is also the burden of having to learn F# first.
  • Overcoming YAML hell: NUKE can generates the CI (TeamCity, Azure Pipelines, AppVeyor, GitHub Actions and more) configuration from the C# build implementation itself.

You can start using these templates right away - but if you’re curious to learn more about how they’re implemented, read on!

For Geeks: Implementation Details

C# DSL

In the past, we used FAKE 4 - which is an F#-based build system.

Our syntax for running a project’s unit tests before looked like this:

Target "RunTests" (fun _ ->
    let projects = 
        match (isWindows) with 
        | true -> !! "./src/**/*.Tests.csproj"
        | _ -> !! "./src/**/*.Tests.csproj" // if you need to filter specs for Linux vs. Windows, do it here

    let runSingleProject project =
        let arguments =
            match (hasTeamCity) with
            | true -> (sprintf "test -c Release --no-build --logger:trx --logger:\"console;verbosity=normal\" --results-directory %s -- -parallel none -teamcity" (outputTests))
            | false -> (sprintf "test -c Release --no-build --logger:trx --logger:\"console;verbosity=normal\" --results-directory %s -- -parallel none" (outputTests))

        let result = ExecProcess(fun info ->
            info.FileName <- "dotnet"
            info.WorkingDirectory <- (Directory.GetParent project).FullName
            info.Arguments <- arguments) (TimeSpan.FromMinutes 30.0) 
        
        ResultHandling.failBuildIfXUnitReportedError TestRunnerErrorLevel.Error result  

    projects |> Seq.iter (log)
    projects |> Seq.iter (runSingleProject)
)

Now, it looks like this using NUKE and C#:

Target RunTests => _ => _
        .Description("Runs all the unit tests")
        .DependsOn(Compile)
        .Executes(() =>
        {
            var projects = Solution.GetProjects("*.Tests");
            foreach (var project in projects)
            {
                Information($"Running tests from {project}");
                foreach (var fw in project.GetTargetFrameworks())
                {
                    Information($"Running for {project} ({fw}) ...");
                    DotNetTest(c => c
                           .SetProjectFile(project)
                           .SetConfiguration(Configuration.ToString())
                           .SetFramework(fw)
                           .SetResultsDirectory(OutputTests)
                           .SetProcessWorkingDirectory(Directory.GetParent(project).FullName)
                           .SetLoggers("trx")
                           .SetVerbosity(verbosity: DotNetVerbosity.Normal)
                           .EnableNoBuild());
                }
            }
        });

Auto-Generation of YAML

Suppose we have the following C# in our NUKE files:

[CustomGitHubActions("pr_validation",
    GitHubActionsImage.WindowsLatest,
    GitHubActionsImage.UbuntuLatest,
    AutoGenerate = true,
    OnPushBranches = new[] { "master", "dev" },
    OnPullRequestBranches = new[] { "master", "dev" },
    InvokedTargets = new[] { nameof(All) },
    PublishArtifacts = true,
    EnableGitHubContext = true)
]

The code above will generate the build configuration YAML below. This is what comes out of the box with the templates.

# ------------------------------------------------------------------------------
# <auto-generated>
#
#     This code was generated.
#
#     - To turn off auto-generation set:
#
#         [CustomGitHubActions (AutoGenerate = false)]
#
#     - To trigger manual generation invoke:
#
#         nuke --generate-configuration GitHubActions_pr_validation --host GitHubActions
#
# </auto-generated>
# ------------------------------------------------------------------------------

name: pr_validation

on:
  push:
    branches:
      - master
      - dev
  pull_request:
    branches:
      - master
      - dev

jobs:
  windows-latest:
    name: windows-latest
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v1
      - name: Make build.sh executable
        run: chmod +x ./build.sh
      - name: Make build.cmd executable
        run: chmod +x ./build.cmd
      - uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 5.0.*
      - uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 6.0.*
      - name: Cache .nuke/temp, ~/.nuget/packages
        uses: actions/cache@v2
        with:
          path: |
            .nuke/temp
            ~/.nuget/packages
          key: $-$
      - name: Run './build.cmd All'
        run: ./build.cmd All
        env:
          GITHUB_CONTEXT: $
  ubuntu-latest:
    name: ubuntu-latest
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Make build.sh executable
        run: chmod +x ./build.sh
      - name: Make build.cmd executable
        run: chmod +x ./build.cmd
      - uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 5.0.*
      - uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 6.0.*
      - name: Cache .nuke/temp, ~/.nuget/packages
        uses: actions/cache@v2
        with:
          path: |
            .nuke/temp
            ~/.nuget/packages
          key: $-$
      - name: Run './build.cmd All'
        run: ./build.cmd All
        env:
          GITHUB_CONTEXT: $
  

Extending the Default Build Configuration

The above code will generate GitHub workflow configuration for Windows and Ubuntu OSes. What if you want to run pr_validation on MacOS too? Well you can as shown below (AutoGenerate is false by default):

[CustomGitHubActions("pr_validation",
    .........
    GitHubActionsImage.MacOsLatest,
    AutoGenerate = true,
    .........)
]

This will generate a build configuration for MacOS if I run build.cmd compile in the root folder, the build configuration will be update:

.....................................
macOS-latest:
    name: macOS-latest
    runs-on: macOS-latest
    steps:
      - uses: actions/checkout@v1
      - name: Make build.sh executable
        run: chmod +x ./build.sh
      - name: Make build.cmd executable
        run: chmod +x ./build.cmd
      - uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 5.0.*
      - uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 6.0.*
      - name: Cache .nuke/temp, ~/.nuget/packages
        uses: actions/cache@v2
        with:
          path: |
            .nuke/temp
            ~/.nuget/packages
          key: $-$
      - name: Run './build.cmd All'
        run: ./build.cmd All
        env:
          GITHUB_CONTEXT: $

Conclusion

Our aim with this release is to improve developers experience when working with Petabridge.Templates - and we are just getting started. We open to suggestion and requests for additional templates. Please file an issue or a suggestion here: https://github.com/petabridge/petabridge-dotnet-new

If you liked this post, you can share it with your followers or follow us on Twitter!
Written by Ebere Abanonu on March 18, 2022

 

 

Observe and Monitor Your Akka.NET Applications with Phobos

Did you know that Phobos can automatically instrument your Akka.NET applications with OpenTelemetry?

Click here to learn more.