Building a workflow for your ASP.NET Core app with Github Actions
Tags: devops, dotnet, ci, githubactions
Published: 2021-05-12 23:33:32


For this example, I merged two workflows provided by GitHub:  *.NET* and *Deploy to Amazon ECS*. The idea is build a simple workflow like this:
![api-deployment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/007jvcyznq98dyka1n95.PNG)

## Why Github Actions?
[Github Actions](https://docs.github.com/en/actions) offers workflows that can build the code in your repository and run your tests. Workflows can run on GitHub-hosted virtual machines, or on machines that you host yourself.

 
To start with Actions, create a new file in the .github/workflows directory named api-deployment.yml and copy the following YAML:

```
name: API workflow

on:
  push:
    branches: [ main ]
    paths:
      - 'src/**'    
  pull_request:
    branches: [ main ]
    paths:
      - 'src/**'

jobs:
  build:
    name: Build API
    runs-on: ubuntu-latest
    env:
      working-directory: './src'
    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-dotnet@v1
      with:
        dotnet-version: '3.1.x'
    - name: Restore dependencies
      run: dotnet restore API.sln
      working-directory: ${{env.working-directory}}
    - run: dotnet build --no-restore
      working-directory: ${{env.working-directory}}    
    - run: dotnet test --no-build --verbosity normal
      working-directory: ${{env.working-directory}}

  deploy-dev:
    name: Deploy to Dev
    if: github.event.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    needs: [build]

    steps:
    - name: Checkout
      uses: actions/checkout@v2

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-2

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1

    - name: Build, tag, and push image to Amazon ECR
      id: build-image
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        ECR_REPOSITORY: your-ecr-name
        IMAGE_TAG: ${{ github.sha }}
      run: |
        # Build a docker container and
        # push it to ECR so that it can
        # be deployed to ECS.
        docker build -f src/api/Dockerfile -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG src/api
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG"

    - name: Fill in the new image ID in the Amazon ECS task definition
      id: task-def
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: .github/task-definitions/your-api-task-definition.json
        container-name: your-container-name
        image: ${{ steps.build-image.outputs.image }}

    - name: Deploy Amazon ECS task definition
      uses: aws-actions/amazon-ecs-deploy-task-definition@v1
      with:
        task-definition: ${{ steps.task-def.outputs.task-definition }}
        service: your-service-name
        cluster: your-cluster-name
        wait-for-service-stability: true

```

That is quite a long file, so let's start to dissect the most important lines.

## Events
An [event](https://docs.github.com/en/actions/reference/events-that-trigger-workflows) is an activity that triggers a workflow. To define the event (or events) we use the **on** keyword. Also, you can add constraints over those triggers.
Here we are setting that our action is going to be triggered on every push or pull request to main, and at least one modified file matches the configured paths.
*Note: push and pull_request events support paths and paths-ignore filter, for more info read [here](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#onpushpull_requestpaths)*

```
on:
  push:
    branches: [ main ]
    paths:
      - 'src/**'    
  pull_request:
    branches: [ main ]
    paths:
      - 'src/**' 
```

## Jobs
A job is a set of steps that execute on the same runner. By default, a workflow with multiple jobs will run those jobs in parallel.

## Build
Build is just a name for this job, you can set whatever you want. Here I'm using mostly the example from the *.NET* workflow. Will restore dependencies, build the code and run the unit tests.
```
  build:
    name: Build API
    runs-on: ubuntu-latest
    env:
      working-directory: './src'
    steps:
    - uses: actions/checkout@v2
    - uses: actions/setup-dotnet@v1
      with:
        dotnet-version: '3.1.x'
    - name: Restore dependencies
      run: dotnet restore API.sln
      working-directory: ${{env.working-directory}}
    - run: dotnet build --no-restore
      working-directory: ${{env.working-directory}}    
    - run: dotnet test --no-build --verbosity normal
      working-directory: ${{env.working-directory}}
```

## Deploy-dev
Deploy-dev job is using the Github workflows templates,
an upcoming post will cover how to create this infrastructure in detail to complete a full deployment. 
But I want to highlight the following block:
```
  deploy-dev:
    name: Deploy to Dev
    if: github.event.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    needs: [build]
```
Using the [needs](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idneeds) keyword we define a dependency to the previous job *build*, only if the build was successfully completed this job is going to be executed. In that form, we change the default behavior of running the jobs in parallel to run sequentially.

On the other way, the [if](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idif) conditional allows avoiding the execution of a job unless a condition is met.
Here we use the [github context](https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions) to get the branch that is triggering the workflow, if the branch is "main" means that the PR was merged, so we can complete our deployment to our dev environment.

So that's it!

You can find a public repo with the example [here](https://github.com/ilaborde/github-workflow)