Automating Umbraco .NET Package Updates in Your GitHub Repository Using Dotnet Outdated

At ilionx, we specialize in simplicity, striving to automate repetitive tasks for greater efficiency. This approach doesn't just apply to our professional work – I also aim to incorporate it into my spare time projects. One area that benefits greatly from automation is keeping your website up to date. In today’s fast-paced digital landscape, regular updates are essential to avoid security vulnerabilities. Failing to stay on top of these updates can expose your site to security risks and compatibility issues.

However, updating your website can be a repetitive task that takes time to prepare. The good news is that we can automate this process to make it simpler and more efficient. In this post, I'll show you how to automate the package update process for your .NET projects using GitHub Actions and the dotnet-outdated tool, helping you maintain a secure and up-to-date project with minimal manual effort.

The Importance of Regularly Updating Packages

Outdated dependencies can cause a variety of issues in a project. These issues include:

  • Security vulnerabilities: Old packages may have known security flaws that could leave your project exposed to attacks.
  • Bug fixes: Developers may have fixed issues in newer versions, improving the stability of your project.
  • New features: Updated packages often come with new features and enhancements that can improve the functionality of your project.

By automating the update process, you ensure that your dependencies stay current with minimal manual effort, reducing the risk of issues caused by outdated packages.

 

Automating Updates with GitHub Actions

GitHub Actions is a powerful automation tool that allows you to set up workflows to automate various tasks in your development pipeline, including package updates. A workflow is essentially a YAML file that defines the steps for a series of actions to be executed in response to a trigger, such as a push to a repository or a scheduled time.

With GitHub Actions, you can automate the process of updating packages by defining a custom workflow that installs the dotnet-outdated tool, checks for outdated packages, updates them, and creates a pull request with the changes. This allows for hands-off dependency management, saving developers time and reducing the risk of human error.

 

 

Breakdown of the Workflow

Triggering the Workflow

 

on:
  schedule:
    - cron: "0 0 25 * *"
  workflow_dispatch:
    inputs:
      version_type:
        description: 'Version Type (Major / Minor)'
        default: 'Major'
        required: true
      target_branch:
        description: 'Target branch'
        default: 'master'
        required: true
      package_list:
        description: 'Comma-separated list of specific packages to update'
        default: 'Umbraco.Cms'
        required: true


The workflow is triggered in two ways:

  1. Scheduled Execution: The schedule event is configured with a cron expression "0 0 25 * *", which runs the workflow on the 25th day of every month at midnight. This ensures that the packages are updated monthly without requiring manual input.

  2. Manual Trigger: The workflow_dispatch event allows you to manually trigger the workflow. When doing so, you can specify:

    • version_type: Whether you want to update major or minor versions of the packages.
    • package_list: A comma-separated list of specific packages to update, with Umbraco.Cms set as the default.

The Workflow Jobs

The workflow defines a single job: auto-update, which runs on the latest version of Ubuntu. Let’s go through the steps involved in this job.

jobs:
  auto-update:
    runs-on: ubuntu-latest


Step 1: Checkout the Current Branch

- name: Checkout the current branch
  uses: actions/checkout@v3


The first step checks out the current branch of the repository using the actions/checkout action. This step is crucial as it ensures the workflow is operating on the latest version of the code.

Step 2: Install the Dotnet Outdated Tool

- name: Install dotnet outdated
  run: |
    sudo apt-get install -y dotnet-sdk-8.0
    dotnet tool install --global dotnet-outdated-tool


In this step, the workflow installs the .NET SDK and the dotnet-outdated-tool. This tool is used to check for outdated NuGet packages in your project. By using this tool, we can easily identify which packages need updating.

About dotnet-outdated

dotnet-outdated is a command-line tool for checking and updating outdated NuGet packages in .NET projects. It simplifies the process of identifying which packages need updating, providing an easy way to keep your project dependencies fresh.

Key features of dotnet-outdated include:

  • Checking for outdated packages in your project.
  • Allowing updates to both major and minor versions of packages.
  • Providing detailed output on which packages are outdated and which version they can be updated to.
  • Allowing specific packages to be updated, making it easier to control the update process.

By using dotnet-outdated in this workflow, you ensure that only the necessary packages are updated, reducing the risk of introducing breaking changes.

Step 3: Generate a Timestamp

- name: Generate timestamp
  id: timestamp
  run: echo "TIMESTAMP=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV


Here, a timestamp is generated and stored in the GitHub environment variables. This timestamp will be used to create a unique branch for the pull request that will be created later.

Step 4: Update Packages Conditionally

- name: Update packages (conditional based on user input)
  run: |
    echo "Updating specific packages: ${{ github.event.inputs.package_list }}"
    for package in $(echo "${{ github.event.inputs.package_list }}" | tr "," "\n"); do
      dotnet outdated -u -vl ${{ github.event.inputs.version_type }} --include $package 
    done


This step is the core of the workflow. It updates the specified packages based on the user’s input:

  • It loops through the list of packages provided by the user.
  • For each package, it runs the dotnet outdated -u command to update the package to the latest version. The -vl flag specifies whether the update is for major or minor versions.

Step 5: Create a Pull Request

- name: Create a PR to the main branch for packages ${{ github.event.inputs.package_list }}
  uses: peter-evans/create-pull-request@v7
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    branch: auto-update-${{ env.TIMESTAMP }}
    commit-message: "Update to latest ${{ github.event.inputs.version_type }} packages ${{ github.event.inputs.package_list }}"
    title: "Auto-update packages to ${{ github.event.inputs.version_type }} versions"
    body: "This PR updates the following packages to the latest ${{ github.event.inputs.version_type }} versions. ${{ github.event.inputs.package_list }}"


The final step in the workflow creates a pull request to update the target branch with the latest package versions. The pull request:

  • Is created on a new branch named auto-update-{timestamp}.
  • Includes a commit message indicating which packages were updated.
  • Contains a description that lists the packages updated in the pull request.

This step uses the peter-evans/create-pull-request action to automate the creation of the PR, making it easy to review the changes before merging.

About peter-evans/create-pull-request

The peter-evans/create-pull-request GitHub Action simplifies the process of creating pull requests from within workflows. It is widely used for automating PR creation in scenarios like dependency updates, documentation changes, or automatically generated content.

Features of this action:

  • Automatically creates pull requests with a configurable title, body, and commit message.
  • Allows you to specify a source branch (e.g., auto-update with a timestamp) and a target branch (usually master or main).
  • Makes it easy to automate tasks like dependency updates, version bumps, and much more without requiring manual intervention.

By using create-pull-request, you can automate the process of updating packages, create a dedicated branch, and ensure that updates are reviewed before being merged into the main codebase.

Why Use This Workflow?

By automating package updates, you can:

  • Ensure your project is always using the latest versions of its dependencies.
  • Avoid potential security vulnerabilities that might arise from outdated packages.
  • Streamline your development workflow by reducing the manual effort required to update packages.

Additionally, the flexibility to choose between major and minor version updates allows for a controlled update process, minimizing the risk of introducing breaking changes.

 

 

The Complete Workflow YAML File

Here’s an full working example YAML file for automating package updates using GitHub Actions and Dotnet Outdated:

name: Auto Update Packages
on:
  schedule:
    - cron: "0 0 25 * *"
  workflow_dispatch:
    inputs:
      version_type:
        description: 'Version Type (Major / Minor)'
        default: 'Major'
        required: true
      package_list:
        description: 'Comma-separated list of specific packages to update'
        default: 'Umbraco.Cms'
        required: true

jobs:
  auto-update:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout the current branch
        uses: actions/checkout@v3

      - name: Install dotnet outdated
        run: |
          sudo apt-get install -y dotnet-sdk-8.0
          dotnet tool install --global dotnet-outdated-tool

      - name: Generate timestamp
        id: timestamp
        run: echo "TIMESTAMP=$(date +'%Y%m%d%H%M%S')" >> $GITHUB_ENV

      - name: Update packages (conditional based on user input)
        run: |
          echo "Updating specific packages: ${{ github.event.inputs.package_list }}"
          for package in $(echo "${{ github.event.inputs.package_list }}" | tr "," "\n"); do
            dotnet outdated -u -vl ${{ github.event.inputs.version_type }} --include $package 
          done

      - name: Create a PR to the main branch for packages ${{ github.event.inputs.package_list }}
        uses: peter-evans/create-pull-request@v7
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          branch: auto-update-${{ env.TIMESTAMP }}
          commit-message: "Update to latest ${{ github.event.inputs.version_type }} packages ${{ github.event.inputs.package_list }}"
          title: "Auto-update packages to ${{ github.event.inputs.version_type }} versions"
          body: "This PR updates the following packages to the latest ${{ github.event.inputs.version_type }} versions. ${{ github.event.inputs.package_list }}"