---
title: "From Zero to Production - Generate everything with a single button"
description: "This blog post will show you how to setup, from scratch, your GitHub repository so you can in a matter of a single click:\n * Run tests and build your application\n * Release the application for example to nuget\n * Create a Release on GitHub with Release notes\n * Update the documentation utilizing GitHub Pages and DocFx\n\nTherefore we will build a \"template\" repository you can take as a foundation."
updated: 2022-04-11
tags: ["GitHub", "CI", "DocFX", "Dotnet", "C#", "Actions", "CD"]
canonical: "https://steven-giesel.com/blogPost/5f9e9f0d-2413-4e4b-8e38-9eebe9503e52/from-zero-to-production-generate-everything-with-a-single-button"
---

# From Zero to Production - Generate everything with a single button

As software engineers, we try to automate as much as possible. That is the reason we built our continuous integration and continuous deployment platform. I will show you how you can utilize **GitHub Actions**, **GitHub Pages**, **DocFx** and of course **.NET** to create your own pipeline that with one click does the following things:
 * Run tests and build your application
 * Release the application for example to nuget
 * Create a Release on GitHub with Release notes
 * Update the documentation utilizing GitHub Pages and DocFx

So let's go through step by step. At the end, I will post the link to the repository if you just want to use this. You will see how to set up the repository, how to automatically generate the documentation, and how then we can release everything with a single button on demand.

## Create the Repository
The first step is not new for most of us. Anyway, it has to be done nevertheless, so let's head over to GitHub and create a new repository.

![repo](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/create_repo.jpg)

I used the default `.gitignore` plus the default `README.md`. As license, I choose `MIT` as I always choose this. All of my public repositories are free to use in private as well as in business projects. But feel free to use whatever license suits you best. Now we just clone the repository to our local PC. I am a heavy user of the git cli, therefore you will see a lot of git command line examples instead of GUI. Please take the tool you are most comfortable with.

`git clone https://github.com/linkdotnet/deployment-template.git` and there you have your new repository.

## The folder structure
Now before I start to fill the repository with live I want to showcase the folder structure I will use throughout this post. Please feel free to adapt this to your needs. First I'll create 4 new folders in the root directory 
* `.github` where the GitHub actions will live
* `docs` where our documentation will live (done by DocFx)
* `src` where our production code will live
* `tests` where our test code will live

## Our library and test project
Now it is time to create a new solution, including a library project and a test project. I will utilize the command line for that. Feel free to use your favorite IDE.

In the root directory, I will use the following commands:
 * `dotnet new sln` - Creates our solution. If you don't provide any arguments the solution will have the same name as the directory it is created. With `-n` you can pass the name for the solution like this: `dotnew new sln -n MyFancySolution` .
 * `dotnet new classlib --output src/MyLibrary` will generate our class library called "MyLibrary" in the `src` directory.
 * `dotnet sln add src/MyLibrary/` will add the newly added project to our solution.
 * `dotnet new xunit --output tests/MyLibraryTests` will create our xUnit test project under `tests/MyLibraryTests`. You can also take `NUnit` or any other testing framework.
 * `dotnet sln add tests/MyLibraryTests` will add the test project to our solution.

The first step is done! You can commit your work and push it to GitHub.

## Optional - StyleCop and SonarAnalyzers
This part is optional and you don't have to use it at all but I use both packages in almost every project I use. The [`StyleCop`](https://github.com/DotNetAnalyzers/StyleCopAnalyzers) analyzers as well as the [`SonarAnalyters.CSharp`](https://github.com/SonarSource/sonar-dotnet) analyzers check your code and warn you if you have a potential issue. But furthermore they can also enforce some coding styles, which is very nice. Another big plus is, that you can configure them to throw an error if a public API is not documented. I highly recommend them to use them, we will see later why the last point is very important.

Those two packages are a bit special to me: I want them literally in **every** project, I even add them to my test projects. Therefore I use the [`Directory.Build.props`](https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2022) file so that all my `csproj` files automatically install those two packages. I have a deep dive about this topic [here in my blog](https://steven-giesel.com/blogPost/f3f46814-06c9-41b7-84fa-09ebb3305ed0).

So I'll add those files. If you want head over to [the repository](https://github.com/linkdotnet/deployment-template) and just copy & paste those 3 files:
 * Directory.Build.props
 * stylecop.analyzers.ruleset
 * stylecop.json

The preset I use is a nice mixture. Please feel free to adopt the rules.

## The library
Until now our library is pretty empty and we have zero tests. Not that great. So we will create a new example class that has one public function. I will use the following function:

```csharp
namespace MyLibrary;

/// <summary>
/// Can print "Hello World" to the user.
/// </summary>
public class HelloWorldPrinter
{
    /// <summary>
    /// Returns "Hello World" <paramref name="times"/> on the Console.
    /// </summary>
    /// <param name="times">How many times Hello World should be displayed.</param>
    /// <returns>"Hello World" multiple times delimited by a newline.</returns>
    public string GetHellWorld(int times)
    {
        var hello = Enumerable.Range(0, times).Select(_ => new string("Hello World"));
        return string.Join(Environment.NewLine, hello);
    }
}
```
As you see I created a new `HelloWorldPrinter` which just returns "Hello World" n times. But also important we have documentation.
This will be very important in a few steps. I would encourage you to document your code (public API in your library, not everything!).

Also add a small test:
```csharp
using MyLibrary;
using Xunit;

namespace MyLibraryTests;

public class UnitTest1
{
    [Fact]
    public void ShouldReturnHelloWorldThreeTimes()
    {
        const string expected = @"Hello World
Hello World
Hello World";
        var sut = new HelloWorldPrinter();

        var str = sut.GetHellWorld(3);

        Assert.Equal(expected, str);
    }
}
```

Let's commit this and move forward.

## The first action: Check the build on commits
Now we have our first test with the first logic. From now on we want to build and test our application with **every, single** commit. For that, we will create a GitHub action. Inside the `.github` folder create a new subfolder called `workflows`. 
And there we create our first action: `dotnet.yml`. You can name it as you wish. I will not go too much into detail about how the actions work. You need a small understanding of how they work, but don't worry. [Here](https://www.youtube.com/watch?v=R8_veQiYBjI) and [here](https://gabrieltanner.org/blog/an-introduction-to-github-actions) for starters can help you out. I found them quite self-explaining:
```yml
name: .NET

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Setup .NET
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 6.0.x
        include-prerelease: true
    - name: Restore dependencies
      run: dotnet restore
    - name: Build
      run: dotnet build --no-restore -c Release
    - name: Test
      run: dotnet test -c Release --no-build
``` 

What it does: On every push to `main` or pull request which targets `main` do the following steps:
 * Checkout our repository to the GitHub Action Agent
 * Restore all our dependencies
 * Build our application
 * And run the tests

Let's commit this and head over to the `Actions` tab in your repository.

![dotnet](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/dotnet_action.jpg))

And everytime the build fails, one of the analyzers sees an issue or one of our tests fail you will get an indication (and even an email if set up). You can create even a status badge which you can put into your README.md. It looks something like this: [![.NET](https://github.com/linkdotnet/deployment-template/actions/workflows/dotnet.yml/badge.svg)](https://github.com/linkdotnet/deployment-template/actions/workflows/dotnet.yml).

## CHANGELOG.md
This is the first part of the documentation. When we release we don't want to write release notes, we want them automatically generated and put into GitHub. For that we will use a file called `CHANGELOG.md` which we will put into the root of the repository. That is not a special file you can name it as you wish, we make it special later ;).

The structure of that file is by convention. The idea is from [keepachangelog](https://keepachangelog.com/en/1.0.0/). When you add a new feature or a fix we will put it into the **Unreleased** section. Once we create a release we leverage this information.

CHANGELOG.md
```
# Changelog

A nice description could be here.

<!-- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) -->

## [Unreleased]

### Added
 - `HelloWorldPrinter` which creates the user

### Fixed
 - Made the world a bit better!
```

The idea is simple, once we release we transform the `Unreleased` part into the current release and tag it as such. So the release should have the following content:
```
### Added
 - `HelloWorldPrinter` which creates the user

### Fixed
 - Made the world a bit better!
```

And our CHANGELOG.md file should look like this afterwards
```
## [Unreleased]

## [1.0.0] - 2022-04-10

### Added
 - `HelloWorldPrinter` which creates the user

### Fixed
 - Made the world a bit better!
```

How this is done I'll show later.

## DocFx - Document our application and API
[**DocFx**](https://dotnet.github.io/docfx) is a static document generator. In our case it will take our C# projects and some markdown and will create a website. Perfect conditions for our free GitHub Page where we can host exact those contents.

Before we create any documentation we need to install **DocFx**. There are multiple ways. I like to install it via **chocolatey** but there are different options. I will just link the [Quickstart guide](https://dotnet.github.io/docfx/tutorial/docfx_getting_started.html#4-use-docfx-with-a-build-server) if you need different setup. With [**chocolatey**](https://chocolatey.org/), which is a package manager for windows, you can just install it via the powershell with elevated rights:
`choco install docfx`. Restart your console and you should be able to enter the following command into your shell: `docfx --version` and should get the current version of **DocFx**. Once we have this we can set up our project.

In the `doc` folder we can create docfx with the following command:

```bash
docfx init -q -o site --apiSourceFolder ../../src/
```
 * `docfx init` will create the project.
 * `-q` means quite so we don't have a lot of console output, you can omit this parameter if you wish
 * `-o site` this will be the output. In this case that would mean all the doc content is under `docs/site`.
 * `--apiSourceFolder ../../src/` we give docfx a hint where our `src` is. This is relative from the newly created `docfx.json` file which lives in `docs/site`.

Now you can go into the new `site` folder and make a small change in the `docfx.json`. Change the first part of "files" from "src/**.csproj" to just "\*\*.csproj". The reason is simple you can in theory host all your code inside the src folder which was created by docfx. I am personally not a big fan of that, but this is up to you.
```json
{
  "metadata": [
    {
      "src": [
        {
          "files": [
            "**.csproj"
          ],
          "src": "../../src/"
        }
      ],
```
Now we are good to go and can have a look at our documentation. For that head again one directory up to `docs` and execute the following command: `docfx site/docfx.json`. This will compile all the files and generates the API Documentation. And afterwards call `docfx serve site/_site`. This will serve the webpage on localhost:8080. It should look something like that.

![docfx](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/docfx_default.jpg)

On the top you also have the API Documentation which should be automatically created. Now I hope it made sense to you that we enforce public API documentation. For convenience I would add a cmd or bash script which does those two commands together in the `docs` folder:
```bash
docfx site/docfx.json
docfx serve site/_site
```

I will not go into details how to write the documentation, but provide you [this link](https://dotnet.github.io/docfx/tutorial/intro_toc.html) where you can get more information. Now is the perfect time in point to commit our work and continue the journey. Take a break if you wish ;).

## Create the GitHub Page
Now that we have the documentation in place, we can make us thoughts where and how we can deploy this. At best we can do this on demand or whenever we have a release. We don't necessarily want this on every commit because then we would show the user new or changed information which doesn't make sense to him at this point in time.

To begin we want to create 2 new branches. One we call `stable` and another one we call `gh-pages`. The latter one is pretty obvious. We use this branch to serve our documentation. `stable` will be our mirror of the latest release **or** the latest release with updated documentation. Now why do we have two branches? One `main` and one `stable`. Just imagine you just want to change your documentation without pushing real content or releasing something, that would be hard with one branch. Therefore we have `stable`. If we only want to update documentation we merge changes into `stable` and `main` (we can also automate that).

![branches](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/branches.jpg)

After we created our branches, we head over to the settings page to the **GitHub Pages** section. There select as **Source** the `gh-pages` branch. You will notice that immediately an action is started.

![github page](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/github_page.jpg)

Afterwards you can click on the link offered to you above the **Source** you changed a few seconds ago. Now that doesn't look that bad but it isn't the content we want to have.

The missing piece is the following: Everytime someone or something pushes to `stable` we want to recompile our documentation. And we do the same as we did locally earlier. So let's head over to the `github/workflow` folder and add a new `yml` file. I will call it `docs.yml`. The action will:
 * Checkout our repository
 * Download docfx
 * Run docfx
 * Publish the changes to the `gh-pages` branch

```yml
name: Docs

on:
  push:
    branches:
    - stable
  workflow_dispatch:

jobs:
  generate-docs:

    runs-on: windows-latest

    steps:
    - uses: actions/checkout@v2

    - name: Setup .NET 6.0
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 6.0.x

    - name: Setup DocFX
      uses: crazy-max/ghaction-chocolatey@v1
      with:
        args: install docfx

    - name: DocFX Build
      working-directory: docs
      run: docfx site\docfx.json
      continue-on-error: false

    - name: Publish
      if: github.event_name == 'push'
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: docs/site/_site
        force_orphan: true
```

Now regarding the `secrets.GITHUB_TOKEN`: This is predefined and you don't have to set this. We will learn/use "real" secrets in just a bit.
Make sure everything you do is on the `main` branch. We will barely interact with the `stable` branch. When you push those changes, you can go to the Github actions and select our newly defined `Docs` workflow via **Run workflow**. After a while you can check again your GitHub page and voila you should see a familiar page. The one from **DocFx** and our API.

![trigger doc](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/trigger_doc.jpg)

Hurray, we did it. We got really far right now. A lot of processes are automated, but we can do more!

## Automatically back-merge `stable` to `main`
The next workflow will automatically back merge any changes from `stable` to main. If the merge was **not** successful we will create a new issue on the very same repository so that the author is aware of the problem and has to resolve the conflict manually.

Create a new workflow file (under `.github/workflows`) named backmerge.yml with the following content:
```yml
name: Back merge to main
on: 
  push:
    branches:
      - stable
  workflow_dispatch:

jobs:
  back-merge:
    if: github.event.pull_request.merged == true
    timeout-minutes: 2
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set Git config
      run: |
          git config --local user.email "actions@github.com"
          git config --local user.name "Github Actions"
    - name: Merge master back to dev
      run: |
          git fetch --unshallow
          git checkout main
          git pull
          git merge --no-ff origin/stable -m "Auto-merge stable back to main"
          git push

    - name: Create issue if failed
      if: failure()
      uses: JasonEtco/create-an-issue@v2
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      with:
        filename: .github/backmerge-failed.md
        update_existing: true
```

The last bit is a bit interesting. If we fail we create a new issue. As template I defined a file under `.github/backmerge-failed.md`. This will be the content of the created issue by the GitHub action. I use a very simple content like this:
```
---
title: Back merge from stable to main failed
labels: input needed
---

The back merge from stable to main failed. Please investigate the GitHub Action and resolve the conflict manually.
```

The `title` will be used as title of the issue and it can also automatically assign labels.

## Set some basic NuGet information in our csproj
We are so close. Stay with me we are almost over the finish line. The next thing which is important is we need an unique ID for our library or application. You can do this via Visual Studio or directly edit your csproj. I use JetBrains Rider for that:

![nuget info](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/NugetCsProj.jpg)

Now set the version to something very low like `0.0.1` and build the project. Also make sure that you generate a nuget package at least once. We need this later to generate the NuGet API Key. After you build the project and have a nuget package, please remove the following line again `<GeneratePackageOnBuild>true</GeneratePackageOnBuild>`. Otherwise this could lead to problems at the end.

## Getting some secrets!
Now we need 2 secrets to add to our repository: Our **NuGet** API so we are allowed to publish packages and a **Personal Access Token**.

### NuGet
Head over to [Nuget](https://www.nuget.org/) to create a new key.

Because we don't have a package now, we have to use upload our earlier created package with a very low version. Don't worry we can directly unlist this afterwards, but API keys are scoped to packages. So if your package is not known, we can't create on. Therefore just upload the earlier created nuget package.

After we uploaded the package, we can directly generate the [token](https://www.nuget.org/account/apikeys):

![nuget api key](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/NugetApiKey.jpg)

Copy the key:

![Copy](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/CopyApiKey.jpg)

Afterwards go to your repository and add the NuGet Key as secret:

![NuGet Secret](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/NugetSecretGit.jpg)

## Personal Access Token

I explained earlier that you already have some kind of access token called `secrets.GITHUB_TOKEN` which we can use for actions, but they have one big problem: Just imagine you have a action which pushes some changes to our `stable` branch. Wouldn't it be nice that our doc action picks this up and pushes the changes to the `gh-pages` branch? Well that only works with a **Personal Access Token** short **PAT**.
For that you have to head over to your [Token](https://github.com/settings/tokens) site and **Generate new token**. Only the `repo` part has to be ticked on for the action we will write.

![PAT](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/create_pat.jpg)

Copy the Token as shown below:

![Pat Copy](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/PatGenerated.jpg)

And at last create a new secret in our repository with the content of our **PAT**. So we should have 2 secrets in our repository.

## Create a release
The last part has come. We bring all the information together and create a release. Now the idea is simple, we have two inputs:
The new version which the user has to provide. Something like `1.0.0` or `1.2.0-beta.2`. We will use this version in the following places:
 * We change **Unreleased** in our **CHANGELOG.md** to the given version
 * The NuGet package will have this version
 * The release will have this version
 * We will create a tag with that version

The other input is if we have a pre-release. There is only one place where this is important: the **GitHub Release Page**. NuGet doesn't care about this flag. NuGet uses [Semantic Versioning](https://semver.org/) to determine if a package is a pre-release or not. Basically everything which has a string after the third digit is considered a pre-release (`1.0.0-rc.1`).

Also once our workflow is done it automatically triggers the updates of the documentation as we merge changes to `stable` as well. Perfect! We are in full control when we release, but once we decide to do so, everything is fully automated.

```yml
name: create-release

on:
  workflow_dispatch:
    inputs:
      versionIncrement:
        description: 'The new version. For example: 1.1.0'
        required: true
        default: ''
      prerelease:
        description: 'Is this a pre-release?'
        type: boolean
        required: false
        default: false 

jobs:
  release:
    name: Publish new release
    runs-on: ubuntu-latest
    steps:

    - name: Checkout repository
      uses: actions/checkout@v2
      with:
        token: ${{ secrets.PAT }}
        persist-credentials: true
        fetch-depth: 0

    - name: Setup dotnet
      uses: actions/setup-dotnet@v2
      with:
        dotnet-version: 6.0.x
    - name: Test
      run: dotnet test -c Release --no-build

    - name: Get changelog entries
      id: changelog
      uses: mindsers/changelog-reader-action@v2
      with:
        version: Unreleased
        path: ./CHANGELOG.md

    - name: Update CHANGELOG file
      uses: thomaseizinger/keep-a-changelog-new-release@1.2.1
      with:
        version: ${{ github.event.inputs.versionIncrement }}

    - name: Set git config
      run: |
        git config --local user.email "linkdotnet@action.com"
        git config --local user.name "LinkDotNet Bot"

    - name: Commit changes and push changes
      run: |
        git add CHANGELOG.md
        git commit -m "Update Changelog.md for ${{github.event.inputs.versionIncrement}} release"
        git push origin main

    - name: Create release on GitHub
      uses: thomaseizinger/create-release@1.0.0
      env:
        GITHUB_TOKEN: ${{ secrets.PAT }}
      with:
        tag_name: v${{ github.event.inputs.versionIncrement }}
        target_commitish: ${{ env.RELEASE_COMMIT_HASH }}
        name: v${{ github.event.inputs.versionIncrement }}
        body: ${{ steps.changelog.outputs.changes }}
        draft: false
        prerelease: ${{ github.event.inputs.prerelease }}

    - name: Create release package
      run: |
        dotnet pack -c RELEASE -p:PackageVersion=${{ github.event.inputs.versionIncrement }} -o ${GITHUB_WORKSPACE}/packages 

    - name: Upload to nuget
      run: |
        dotnet nuget push ${GITHUB_WORKSPACE}/packages/*.nupkg -k ${{ secrets.NUGET_KEY }} -s https://api.nuget.org/v3/index.json --skip-duplicate --no-symbols

    - name: Merge main to stable
      run: |
        git fetch
        git checkout stable
        git pull
        git merge --no-ff -X theirs origin/main -m "Updating to newest release"
        
    - name: Push changes
      uses: ad-m/github-push-action@master
      with:
        github_token: ${{ secrets.PAT }}
        branch: stable
        force: true
```

A few details here:


```yml
- name: Push changes
      uses: ad-m/github-push-action@master
      with:
        github_token: ${{ secrets.PAT }}
        branch: stable
        force: true
```
We are using our **PAT** so that all actions, as explained on top, will trigger.

```yml
    - name: Create release on GitHub
      uses: thomaseizinger/create-release@1.0.0
      env:
        GITHUB_TOKEN: ${{ secrets.PAT }}
      with:
        tag_name: v${{ github.event.inputs.versionIncrement }}
        target_commitish: ${{ env.RELEASE_COMMIT_HASH }}
        name: v${{ github.event.inputs.versionIncrement }}
        body: ${{ steps.changelog.outputs.changes }}
        draft: false
        prerelease: ${{ github.event.inputs.prerelease }}
```

This will create the release with the given tag defined as our variable. We will add `v` in fron of it stick to the "norm". We can also see that we use the content of **CHANGELOG.md** to fill the `body` of our release page.

### Results

If you commit this file and head over to the actions panel.

![create release](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/create_release.jpg)

After it successfully run through, we can see we have a new commit on main, plus a new entry in the release section of our repository:

![Release](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/NugetRelease.jpg)

Let's have a look at our **CHANGELOG.md**:
```
# Changelog

A nice description could be here.

<!-- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) -->

## [Unreleased]

## [1.0.0] - 2022-04-10

### Added

-   `HelloWorldPrinter` which creates the user

### Fixed

-   Made the world a bit better!

[Unreleased]: https://github.com/linkdotnet/deployment-template/compare/1.0.0...HEAD

[1.0.0]: https://github.com/linkdotnet/deployment-template/compare/902a59583c17b4e0c437e156c038bd25ac2958f0...1.0.0
```

Also perfect! The last thing which is important is that our package got published on NuGet:

![NuGet](https://linkdotnetblog.azureedge.net/blog/20220410_GithubActions/uploaded_package.jpg)

That is perfect! We can also put a checkmark behind that task. And that is basically it. With a matter of a button click we can release a package, create the information and tag on GitHub and update our documentation as well.
Of course, you can adopt these workflows as you wish.

## Resources
 * The template GitHub Repository can be found [here](https://github.com/linkdotnet/deployment-template)