Configure Renovate on Azure DevOps mono-repo

Configure Renovate on Azure DevOps mono-repo

Today i will be providing details about setting up Renovate via Azure DevOps CI/CD pipeline which runs on monthly basis. Here you can see how automation of dependency management takes place in Enterprise environments where you have software, infrastructure and other possible code configurations.

Before we dive deep let's just take short brief on what is Renovate and why should we use it. Renovate is an automated dependency update tool commonly used in software development workflows. It helps streamline the process of keeping project dependencies up-to-date by automatically identifying outdated dependencies in code repositories and generating pull requests to update them. Renovate supports a wide range of package managers and platforms, including npm, Maven, Docker, and GitHub repositories. By automating dependency updates, Renovate reduces the burden on developers to manually track and update dependencies, ensuring projects stay secure, compatible, and up-to-date with the latest features and bug fixes.

Now that we have understanding what is Renovate and why we want to implement it, let's see how we can configure it. First clone the repo that you want to automate with Renovate, or if you already did that, create new branch where you will implement Renovate. First and most important thing. Mono-repo is configured in a way that holds multiple code bundles, now in order to make it operational we need to visualize the repo. Here is what we have as a baseline.

monorepo/
├── app/
├── infrastructure/
└── configuration/

Our first step is going to implement 3 files , renovate.json which is configuration file on how Renovate should behave, next we will have config.js which will store target destination in terms of platform that we use as a repository, it can be Github, bitbucket, Azure DevOps etc. and last we need pipeline which we've call renovate-pipeline.yaml, this pipeline will run all of this logic. Don't worry, i will be providing content of these files so you can see what is going on in there. After implementing these 3 files your project structure should look something like this.

monorepo/
├── app/
├── infrastructure/
├── configuration/
|- config.js
|- renovate-pipeline.yaml
|- renovate.json

Now very important thing! All these 3 files needs to sit at the root of the project otherwise pipeline will not be able to see config.js file, even though there is an option to specify config.js path, Renovate is not able to see it, so if you store this into app/ or infrastructure/, Renovate will fail, stating that authentication is wrong, and if you open logs, it says that it switches to Github target , even we didn't configure it in such a fashion, I spent 3 days roaming around this, that is the reason why I'm writing this article today, to save you pain and grind.

Here is the config.js file

module.exports = {
  platform: 'azure',
  endpoint: 'https://dev.azure.com/<organization_name>/',
  token: process.env.TOKEN,
  hostRules: [
    {
      hostType: 'npm',
      matchHost: 'pkgs.dev.azure.com',
      username: 'apikey',
      password: process.env.TOKEN,
    },
  ],
  repositories: ['<project_name>/<repo_name>'],
};

Here we can clearly see that platform in azure, endpoint directs to organization of your desire and host-rules block of code is stating which artifactory your are targeting. In this case we will update npm packages. At the end, we have defined repository that needs to be updated.

Next we will have renovate-pipeline.yaml.

schedules:
  - cron: '0 02 8-14 * sat' 
    displayName: 'Every second Saturday of the month at 2am'
    branches:
      include: [main]
    always: true

trigger: none

pool: '<agent-pool>'

steps:
  - task: npmAuthenticate@0
    inputs:
      workingFile: .npmrc

  - task: NodeTool@0
    inputs:
      versionSpec: '18.x'
    displayName: 'Install Node.js'

  - script: npm install npx
    displayName: 'npm install npx'

  - bash: |
      git config --global user.email 'bot@renovateapp.com'
      git config --global user.name 'Renovate Bot'
      npx --userconfig .npmrc renovate
      LOG_LEVEL=debug npx --userconfig .npmrc renovate
    env:
      TOKEN: $(System.AccessToken)
      RENOVATE_TOKEN: $(System.AccessToken)

Here we have first authentication to artifactory with npmAuthenticate@0 task, he will look for information about artifactory url in the .npmrc file, next it will install node 18 so latest Renovate can run, and then it will install npx so it can run bash script that fires up Renovate. In the bash section we have configuration of the Renovate bot username and email and then we have command for running renovate. In case things go south for you i have added LOG_LEVEL=debug npx --userconfig .npmrc renovate

This line will ensure to provide you with verbose output in at the runtime, so you can see if something is bad for you. This is how i figured out potential missing config.js file.

At last we have tokens that are generated on the fly that has been used by project-collection build service. The "Project Collection Build Service" in Azure DevOps is a system identity that represents the build service in your Azure DevOps organization. It's automatically created and used by Azure DevOps to run builds and access resources within your organization.

When you set up authentication for Azure DevOps pipelines, you might encounter the need to grant permissions, and these permissions in the picture below. Beside these 3, you need to grant Read rights as well, and this can be achieved by opening project that you want to automate with Renovate so Project - > Project Settings -> Repository name -> Repository -> Security and then you will see page where you can configure permissions for the system identity.

If this is not configured, Renovate will fail, so before running anything, this should be done before. Last thing that we have is renovate.json. This file tells Renovate how to behave. If you have only application folder, there is general file for that which you can find below.

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "branchPrefix": "renovate/update-",
  "groupName": "all dependencies",
  "groupSlug": "all",
  "lockFileMaintenance": {
    "enabled": false
  },
  "packageRules": [
    {
      "groupName": "all dependencies",
      "groupSlug": "all",
      "matchPackagePatterns": ["*"]
    },
    {
      "matchPackageNames": ["@types/node"],
      "allowedVersions": "< 18.18.0"
    }
  ],
  "separateMajorMinor": false
}

Place this at the root of your project and you will be good to go. However if you have mono-repo, you will encounter mess, this file that you can see above it will scan all folders and it will create one big chunky PR with all dependencies, and you will not know what needs to be updated and what not, now in order to avoid this, i have created config file that will help you alleviate this, by creating separate PR per folder that you have.

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "branchPrefix": "renovate/update-",
  "groupName": "all dependencies",
  "groupSlug": "all",
  "lockFileMaintenance": {
    "enabled": false
  },
  "packageRules": [
    {
      "groupName": "app",
      "groupSlug": "function-app dependancies",
      "matchFiles": ["function-app/package.json"],
      "matchPackagePatterns": ["*"],
      "labels": ["root", "Renovate"]
    },
    {
      "groupName": "infrastrucutre",
      "groupSlug": "Terraform module dependancies",
      "matchFiles": ["infrastructure/"],
      "matchPackagePatterns": ["*"],
      "labels": ["module", "Renovate"]
    },
    {
      "groupName": "Configuration",
      "groupSlug": "config dependancies",
      "matchFileNames": ["Configuration/"],
      "matchPackagePatterns": ["*"],
      "labels": ["terraform", "Renovate"]
    },
    {
      "matchPackageNames": ["@types/node"],
      "allowedVersions": "< 18.18.0"
    }
  ],
  "separateMajorMinor": false
}

As described above Renovate will target app, infrastructure and configuration folder where it will create two separate PRs. The PRs will be named using a combination of branchPrefix + groupSlug. For the application, package.json needs to be targeted as he is using that file to look for dependencies, for infrastructure and configuration, no specific dependency file needs to be defined. Renovate is smart enough to go trough the file and scan them standalone.

After all of this is configured, you need to run pipeline. Submit PR and go to Azure DevOps where you will be creating new pipeline. You will have option to specify your branch upon pipeline selection. After successful run of the pipeline you should get PR by Renovate

Approve the PR if you are satisfied, and after this Renovate will run periodically as it was defined in the Azure DevOps Pipeline. Hope this article helps you configure Renovate on Mono-Repo and provide you with greater security and stability. Let me know in the comments if this helps you with your task!

Managing dependencies across multiple projects within a mono-repository can be challenging. Renovate simplifies this process by automating dependency updates. With Renovate, you can effortlessly track, monitor, and update dependencies across your entire codebase. This blog post explores how Renovate can help you maintain up-to-date dependencies, improve security, and streamline your development workflow in a mono-repository setup. Discover how Renovate can save you time and effort while keeping your projects in sync with the latest dependencies."