DEV Community

catatsuy
catatsuy

Posted on

Track MySQL LTS Updates with Renovate

In private-isu, we used MySQL 8.4 in Docker Compose.

mysql:
  image: mysql:8.4
Enter fullscreen mode Exit fullscreen mode

However, the default Renovate configuration created a pull request to update MySQL to 26.7.

https://github.com/catatsuy/private-isu/pull/1107

- image: mysql:8.4
+ image: mysql:26.7
Enter fullscreen mode Exit fullscreen mode

MySQL 26.7 is an Innovation release. We do not want to follow the latest release. We want to follow the latest LTS release.

To do this, I added a Renovate custom datasource that tells Renovate which version is the current MySQL LTS.

https://github.com/catatsuy/private-isu/pull/1108

Why not use mysql:lts?

The official MySQL Docker image provides an lts tag.

mysql:
  image: mysql:lts
Enter fullscreen mode Exit fullscreen mode

However, lts is a mutable tag.

When lts changes from 8.4 to 9.7, nothing changes in the Compose file. The next time the image is pulled, the MySQL release series may change without anyone noticing.

We do not want to automatically follow the lts tag at runtime. We want Renovate to notify us with a pull request when the LTS version changes.

- image: mysql:8.4
+ image: mysql:9.7
Enter fullscreen mode Exit fullscreen mode

Reading the Docker Official Images file

Docker Official Images manages the MySQL tags in this file:

https://github.com/docker-library/official-images/blob/master/library/mysql

The file is not JSON and does not come from an API. It is plain text.

Tags: 26.7.0, 26.7, 26, innovation, latest, ...
Tags: 9.7.2, 9.7, 9, lts, ...
Tags: 8.4.11, 8.4, 8, ...
Enter fullscreen mode Exit fullscreen mode

Renovate reads this file, finds the line containing lts, and extracts 9.7.

Reading text with format: "plain"

I configured the custom datasource like this:

{
  "customDatasources": {
    "mysql-lts": {
      "defaultRegistryUrlTemplate": "https://raw.githubusercontent.com/docker-library/official-images/master/library/mysql",
      "format": "plain",
      "transformTemplates": [
        "( $ltsLine := releases.version[$substring($, 0, 6) = 'Tags: ' and $contains(', ' & $substringAfter($, 'Tags: ') & ', ', ', lts, ')][0]; $minor := $split($substringAfter($ltsLine, 'Tags: '), ', ')[$match($, /^\\d+\\.\\d+$/)][0]; { 'releases': $minor ? [{ 'version': $minor }] : [] } )"
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The important part is format: "plain".

The source is not structured JSON. With plain, Renovate reads the file line by line and roughly converts it into this structure:

{
  "releases": [
    {
      "version": "Tags: 26.7.0, 26.7, 26, innovation, latest, ..."
    },
    {
      "version": "Tags: 9.7.2, 9.7, 9, lts, ..."
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

The data is then processed by transformTemplates.

Extracting the LTS version with JSONata

transformTemplates uses JSONata.

JSONata is a language for querying and transforming JSON data. You can think of it as a way to write a query and a transformation in one expression.

The expression does four things:

  1. Find a line that starts with Tags: and contains lts
  2. Split the line by commas
  3. Find a version such as 9.7 that matches number.number
  4. Return it in Renovate’s release format

The result looks like this:

{
  "releases": [
    {
      "version": "9.7"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Renovate can then compare 9.7 with the 8.4 version in the Compose file and create an update pull request.

Disable the standard Docker manager

The standard Docker Compose manager also detects the MySQL image in the Compose file.

Without extra configuration, the custom datasource suggests 9.7, while the standard Docker datasource suggests 26.7.

To avoid this, I disabled the standard manager only for MySQL in this file.

{
  "matchManagers": ["docker-compose"],
  "matchDatasources": ["docker"],
  "matchPackageNames": ["mysql"],
  "matchFileNames": ["webapp/compose.yml"],
  "enabled": false
}
Enter fullscreen mode Exit fullscreen mode

Disable minimumReleaseAge

private-isu has this global setting to avoid versions immediately after release:

{
  "minimumReleaseAge": "2 days"
}
Enter fullscreen mode Exit fullscreen mode

However, this custom datasource does not return release dates.

Without another rule, the update remains pending. I therefore set minimumReleaseAge to 0 days only for this datasource.

{
  "matchDatasources": ["custom.mysql-lts"],
  "matchPackageNames": ["mysql"],
  "matchFileNames": ["webapp/compose.yml"],
  "minimumReleaseAge": "0 days"
}
Enter fullscreen mode Exit fullscreen mode

Renovate created the expected LTS update

After merging the configuration, Renovate created the expected pull request.

https://github.com/catatsuy/private-isu/pull/1109

- image: mysql:8.4
+ image: mysql:9.7
Enter fullscreen mode Exit fullscreen mode

Instead of using mysql:lts directly, Renovate watches the version referenced by the lts tag.

This prevents the MySQL release series from changing without a visible code change.

Pinning the patch version

In private-isu, we pin the image to a version such as 9.7.

image: mysql:9.7
Enter fullscreen mode Exit fullscreen mode

With this format, an update from 9.7.1 to 9.7.2 does not change the Compose file.

To review patch updates in pull requests, the same approach can extract the full version, such as 9.7.2.

Change the JSONata version pattern to:

^\d+\.\d+\.\d+$
Enter fullscreen mode Exit fullscreen mode

The Compose configuration must also read the full version:

(?<currentValue>\d+\.\d+\.\d+)
Enter fullscreen mode Exit fullscreen mode

Renovate can then create pull requests for patch updates.

- image: mysql:9.7.1
+ image: mysql:9.7.2
Enter fullscreen mode Exit fullscreen mode

Use 9.7 when you only want to review changes to the LTS release series. Use 9.7.2 when you also want to review patch updates.

Renovate custom datasources make it possible to track mutable tags such as lts and stable without using those tags at runtime. Instead, changes to the version behind the tag are managed as pull requests.

Top comments (0)