Using GitHub Actions to Update .NET Dependencies Automatically
Tags: github, dotnet, nuget, ci
Published: 2021-11-27 20:45:31

It’s a tale as old as time, as a software engineer, you write some code, you’re happy with it and then you deliver it. End of story, right? No more work to be done! As usual there’s many more layers to deal with and one of those many layers is maintenance……boooooo!!!!!

I know this isn’t the most amazing topic to write about, but it solves a real-world problem for me and I’m hoping for some of you too.

I’m a backend engineer and write a lot of C#, which means I end up with solutions relying on NuGet packages. These do need to be updated all the time because there are other engineers out there doing amazing work keeping their packages up to date and I like to keep my dependencies up to date. This is where I find myself, with the problem of having to create a branch, update the packages, commit it, then seek PR approval, just to get some minor updates through. It’s annoying and I want to solve this.

Before we get underway, are these instructions for me?

1. You have a solution written in .NET…these instructions only work for code in .NET.
1. The code already is, or you are about to host this solution in Github….so you can use Actions.
1. You want to update your NuGet packages, engineers have valid reasons for doing or not doing things, no pressure here. I like to keep my packages up to date or at the very least alert me to when I need to make changes because of dependency updates.

If you are in, it’s time to work with me here, Actions can be used to trigger many workflows. The most common being build and release pipelines for you project. The answer to our problem also lives in an Action. The time has come to create a new one.

1. Navigate to your GitHub Repository of choice
1. Select the Actions tab
1. Click that New Workflow button
1. Ignore all the templates and select “Set up a Workflow Yourself”
1. Delete all the sample code and we are good to go

![Create a New Black GitHub Action](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vvaw7lgazrdf1yct39nz.gif)

---

## Step 1: Create a schedule action

This is the easiest part, this action will run once a week. You can change the schedule to anything you want. GitHub Action don’t run perfectly on time, but the do run. For more on scheduled jobs and setting the schedules, read the [GitHub Documentation](https://docs.github.com/en/actions/learn-github-actions/events-that-trigger-workflows#schedule) and checkout the examples at [Crontab Guru](https://crontab.guru/examples.html).

```
name: Automated Dependency Updates
on:
    schedule:
        - cron:  '0 0 * * 0'
```

---

## Step 2: Setup the Job

Next up we set up the job. This is where we define what steps we want to run. We’re running this job on the latest version of Ubuntu. Next, we setup the default `actions/checkout` step as well as `actions/setup-dotnet` to install the version of the .NET SDK we need for our solution. I need .NET 6 for this job. You must that notice the changed token for the checkout `AUTOMATED_DEPENDENCY_UPDATES_TOKEN`, this comes in handy in Step 5, I’ll explain what’s up down there.

```jobs:
    dependencies: 
        name: Update Dependencies
        runs-on: ubuntu-latest
steps:
    - name: Checkout
      id: checkout
      uses: actions/checkout@v2
      with:
          token: ${{ secrets.AUTOMATED_DEPENDENCY_UPDATES_TOKEN }}
    - name: Setup
      uses: actions/setup-dotnet@v1
      with:
          dotnet-version: 6.0.x
```

---

## Step 3: Update those Dependencies

These are the base steps of updating our NuGet packages. First, we need to install the [dotnet-outdated tool](https://github.com/dotnet-outdated/dotnet-outdated) and then validate if we need to take any further action. If we run `dotnet outdated` and there are not packages to update we should cancel the rest of the steps in this job.
This is done but setting an output variable on the validate step that can be used by every other step in the job to determine if it should run or not. If there are no updates to be made, then we set the output variable to `false`.
If we have updates to make them, we run `dotnet outdated -u` to force the NuGet updates. If this is successful, then we set the output variable to `true` to allow the following steps to run.

```
- name: Tools
  id: tools      
  run: dotnet tool install --global dotnet-outdated-tool     
- name: Update      
  id: update      
  run: |        
      OUTPUT=$(dotnet outdated)        
      if [[ $OUTPUT =~ "No outdated dependencies" ]]; then
          echo "::set-output name=updated::false"        
      else            
          dotnet outdated -u                    
          echo "::set-output name=updated::true"        
      fi      
  shell: bash
```

---

## Step 4: Run your Tests
After you’ve updated the NuGet packages you need to validate that the changes haven’t broken your code. The best way to do that is to build and test your solution. It’s a simple `dotnet test` command to run all the tests on the solution. You can update this statement to suit your needs, the default should be to run all your tests.

```
- name: Test
  id: test
  if: ${{ steps.update.outputs.updated == 'true' }}
  run: dotnet test -c Release -v minimal
```

---

## Step 5: Commit and Push

The final step is to commit and then push the NuGet package updates, if the tests pass then this step runs and your automated package update is complete. When we used the `AUTOMATED_DEPENDENCY_UPDATES_TOKEN` in the checkout step, that was to step around the fact that the inbuilt `GITHUB_TOKEN` will not trigger a follow-on workflow. If you generate a specific [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) to use when checking the code out then after the code is pushed, if you have another automated workflow, it will trigger.
Finally, we set the commit message and author. This will register the commit to the GitHub Action user and not the user who setup the job.

```
- name: Push
  id: push
  if: ${{ steps.update.outputs.updated == 'true' }}
  uses: stefanzweifel/git-auto-commit-action@v4
  with:
      commit_author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
      commit_user_name: github-actions[bot]
      commit_user_email: 41898282+github-actions[bot]@users.noreply.github.com
      commit_message: Automated Dependency Updates
```

---

## We’re Done!

Now let’s put all the steps together in one easy Gist to use in any other repository. Hope this helps you automate that boring task too. This can work for any other CI/CD tool that allows for scheduled jobs. The concepts are the same just follow the Checkout, Update, Test and Push process.

{% gist https://gist.github.com/stphnwlsh/86dfb8b3741086fef83fda7bd65fb29b %}

---

## Support

If you like this, checkout my other examples on [GitHub](https://github.com/stphnwlsh) and consider supporting me at [Buy Me a Coffee](https://www.buymeacoffee.com/stphnwlsh).

[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-1.svg)](https://www.buymeacoffee.com/stphnwlsh)