Light Mode Image
How to setup CI/CD pipeline using self-hosted GitHub Actions
D

Dipendra Neupane

How to setup CI/CD pipeline using self-hosted GitHub Actions

    Back in 2018, GitHub introduced a platform-native automation tool called GitHub actions.

     

    GitHub users can use Actions to build their continuous integration and continuous delivery pipelines. And that’s pretty much the first thing most people think about when they hear about this tool. You can utilize this precious feature of GitHub to automate day-to-day tasks.

     

    In this article, we’ll be creating a bare minimum Asp.Net Core Web API including an NUnit test project and setting up CI/CD pipeline using self-hosted GitHub actions. Here are a few key terms you must know before getting started with the GitHub actions pipeline.

     

    GitHub actions workflow

    GitHub actions workflow is the configurable automated process written on a .yml file to run one or more jobs.

     

    It will be checked into the repository and will be triggered when a specified event happened on a repository. A repository can have multiple workflow files and each of which can perform a different set of tasks.

    Workflow files are defined in the .github/workflows directory within a repository.

     

    GitHub Action's Runner

    A runner is an application that runs the job defined on the GitHub actions workflow file. GitHub provides its GitHub-hosted runner or you can create your self-hosted runner.You can add a self-hosted runner to a repository, an organization, or an enterprise. We will be adding a self-hosted runner to a repository for this demo.

     

     

    GitHub provides a bunch of commands to install a runner on your machine. The runner should be running to pick the job when a new event happens on the repository. You can run a runner as a windows service. Please make sure the command prompt is opened in administrator mode while installing it.

     

     

    After installation, the self-hosted GitHub actions runner for my repository is running on my local window’s service. This is how it looks like on GitHub settings.

     

     

    Now our self-hosted GitHub runner is ready to run our GitHub actions workflow file. Creating a workflow file is easy with the action menu on the repository itself. GitHub has a bunch of pre-defined workflow file templates that you can choose and edit according to your requirement. I have searched for .net and edited that template for our use.

     

    name: AspNetCoreAPI
    
    on:
      push:
        branches: [ "master" ]
      pull_request:
        branches: [ "master" ]
    
    jobs:
      build:    
        runs-on: [self-hosted]
    
        steps:
        - uses: actions/checkout@v1
    
        - name: Set up .NET Core
          uses: actions/setup-dotnet@v1
          with:
            dotnet-version: '6.0.x'
    
        - name: Build with dotnet
          run: dotnet build src\AspNetCoreAPI-GitHubAction.Api\AspNetCoreAPI-GitHubAction.Api.csproj --configuration Release
    
      test:
        needs: build
        runs-on: [self-hosted]
    
        steps:
        - name: Test
          run: dotnet test src\AspNetCoreAPI-GitHubAction.Test
    
      deploy:
        needs: test
        runs-on: [self-hosted]
        
        steps:
        - name: Project publish
          run: dotnet publish -c Release src\AspNetCoreAPI-GitHubAction.Api\AspNetCoreAPI-GitHubAction.Api.csproj -o ${{env.DOTNET_ROOT}}/AspNetCoreAPI
    
        - name: Deploy to IIS
          run: |
            if ((Get-WebSiteState -Name AspNetCoreAPI).Value -eq "Started")
            {
                Stop-WebSite -Name AspNetCoreAPI
                echo "Stopped Website AspNetCoreAPI"
            }
            if ((Get-WebAppPoolState -Name AspNetCoreAPI).Value -eq "Started")
            {
                Stop-WebAppPool -Name AspNetCoreAPI
                echo "Stopped Application Pool AspNetCoreAPI"
            }
            
            Start-Sleep -s 15        
            Copy-Item ${{env.DOTNET_ROOT}}/AspNetCoreAPI/* C:\inetpub\wwwroot\AspNetCoreAPI -Recurse -Force
            
            if ((Get-WebSiteState -Name AspNetCoreAPI).Value -eq "Stopped")
            {
                Start-WebSite -Name AspNetCoreAPI
                echo "Started Website AspNetCoreAPI"
            }
            if ((Get-WebAppPoolState -Name AspNetCoreAPI).Value -eq "Stopped")
            {
                Start-WebAppPool -Name AspNetCoreAPI
                echo "Started Application Pool AspNetCoreAPI"
            }
            
            if ($lastexitcode -lt 8) { $global:lastexitcode = 0 }

     

    This is the YAML file with file extension .yml defined inside the .github/workflows directory as mentioned earlier. We also name it a workflow file. Let’s break it down into smaller pieces to understand it more clearly.

     

    on:
      push:
        branches: [ "master" ]
      pull_request:
        branches: [ "master" ]

     

    It has pre-defined jobs that need to be executed when a specified event occurs. i.e, when a developer pushes any changes or makes a new pull request on the master branch, then the runner will execute this workflow file.

     

    build:    
        runs-on: [self-hosted]
    
        steps:
        - uses: actions/checkout@v1
    
        - name: Set up .NET Core
          uses: actions/setup-dotnet@v1
          with:
            dotnet-version: '6.0.x'
    
        - name: Build with dotnet
          run: dotnet build src\AspNetCoreAPI-GitHubAction.Api\AspNetCoreAPI-GitHubAction.Api.csproj --configuration Release

     

    The jobs are defined in three steps, build, test, and deploy. All of these steps will run one after another. This step will build the .csproj with Dotnet version 6 or above.

     

    actions/checkout@v1 checks out the repository under GitHub workspace, so the workflow can access it.

    actions/setup-dotnet@v1 will configure the .NET SDK on our runner so that we can use the .NET CLI command.

     

    test:
      needs: build
      runs-on: [self-hosted]
    
      steps:
      - name: Test
        run: dotnet test src\AspNetCoreAPI-GitHubAction.Test 

     

    The needs: build command on the test step wants the previous build step to complete successfully before the test step gets triggered. It also makes sense that each job will only run if the previous job runs successfully.

     

    deploy:
      needs: test
      runs-on: [self-hosted]
      
      steps:
      - name: Project publish
        run: dotnet publish -c Release src\AspNetCoreAPI-GitHubAction.Api\AspNetCoreAPI-GitHubAction.Api.csproj -o ${{env.DOTNET_ROOT}}/AspNetCoreAPI
    
      - name: Deploy to IIS
        run: |
          if ((Get-WebSiteState -Name AspNetCoreAPI).Value -eq "Started")
          {
              Stop-WebSite -Name AspNetCoreAPI
              echo "Stopped Website AspNetCoreAPI"
          }
          if ((Get-WebAppPoolState -Name AspNetCoreAPI).Value -eq "Started")
          {
              Stop-WebAppPool -Name AspNetCoreAPI
              echo "Stopped Application Pool AspNetCoreAPI"
          }
          
          Start-Sleep -s 15        
          Copy-Item ${{env.DOTNET_ROOT}}/AspNetCoreAPI/* C:\inetpub\wwwroot\AspNetCoreAPI -Recurse -Force
          
          if ((Get-WebSiteState -Name AspNetCoreAPI).Value -eq "Stopped")
          {
              Start-WebSite -Name AspNetCoreAPI
              echo "Started Website AspNetCoreAPI"
          }
          if ((Get-WebAppPoolState -Name AspNetCoreAPI).Value -eq "Stopped")
          {
              Start-WebAppPool -Name AspNetCoreAPI
              echo "Started Application Pool AspNetCoreAPI"
          }
          
          if ($lastexitcode -lt 8) { $global:lastexitcode = 0 }

     

    I have already set up the project on IIS having the name AspNetCoreAPI and the same for the Application pool as well.

     

    Before moving the published file to the wwwroot directory, the first step will publish the project on the directory inside AspNetCoreAPI at DOTNET_ROOT. Later on, the next step will copy the published files to the pre-build directory inside wwwroot. Application and application pool are stopped before moving the file so that copied files move without an issue.

     

     

    Now my deployment process is automated using the GitHub actions pipeline. Any new push on the master branch will automatically publish the changes. These green ticks are always satisfying in software development and everything looks perfect.

     

    Here are workflow commands for GitHub actions and this is the GitHub repository that I have used for this article.

    04 Dec 2023
    By Dipendra Neupane
    ;