One less experimentation: Autodiscovery

Posted December 12, 2023 by olblak ‐ 6 min read

When we started working on the "Autodiscovery" feature, our objective was to stop writing and maintaining Updatecli manifests for environments, where we could automate that process.

Automate, automate, automate…​

So we wonder, what if we had a pipeline generator for Updatecli that would search for "standard" update scenarios using the same "plugin" approach we’ve been using since the beginning of Updatecli

And that’s what we did…​

We decided to build a small set of plugins that would cover most of our needs:

And it turned out to be a great learning experience. Combining both the Autodiscovery and the declarative approach allowed us to achieve a high level of automation across different types of git repositories like dev, gitops, or simply documentation.

Autodiscovery

Early, we realized the Autodiscovery feature had to come with some level of configuration.

Default

With the default settings, the Autodiscovery can be used to quickly analyze local directories.

Assuming that no files named updatecli.yaml or updatecli.d are in the current directory, we can:

  • Show available pipeline that Updatecli detects by running:

updatecli manifest show
  • Show a diff of what Updatecli can update by running:

	updatecli diff
  • Apply changes locally by running:

  updatecli apply

Unfortunately, the default behavior has many limitations such as not being able to detect pull request configuration, dependencies to ignore, dependencies update to group, etc. So sooner than later we need to provide additional configuration.

Configuration

Default behavior is nice but we pretty much always need some level of configuration like in the following example:

name: "deps: bump patch version for Golang package defined in go.mod"
pipelineid: "golang/gomod/patch"

scms:
  default:
    kind: github
    spec:
      owner: updatecli
      repository: updatecli
      token: '{{ requiredEnv "GITHUB_TOKEN" }}'
      username: '{{ requiredEnv "GITHUB_ACTOR" }}'
      branch: main

actions:
    default:
        kind: github/pullrequest
        scmid: default
        spec:
          # Opening one pull request per patch version update can generate
          # a lot of noise, so considering how low-risk patch updates are,
          # we want to automatically merge them as long as tests are passing.
          automerge: true
          # Pullrequest labels can have a lot of meanings.
          # For example the tool "Release Drafter" relies on them to automatically
          # generate release changelog.
          labels:
            - "dependencies"
            - "golang"

autodiscovery:
  scmid: default
  actionid:  default
  # Projects with a lot of dependencies can have a lot of patch version update
  # so considering how low-risk they are, we want to group them together into
  # one pull request.
  groupby: all
  crawlers:
    golang/gomod:
      versionfilter:
        kind: semver
        # We only want to update patch version updates and handle minor and major updates with a different update pipeline.
        pattern: patch
      ignore:
        - modules:
            # Ignoring the following modules as they do not publish release
            github.com/ProtonMail/go-crypto: ""
            # Ignoring the following modules as they do not publish release
            github.com/shurcooL/githubv4: ""
            # A lot of activity happens on github.com/aws/aws-sdk-go which resulting into
            # multiple releases per day. Considering we have good testing in place
            # we prefer to use the Updatecli declarative approach to specifically handle this library
            # update.
            github.com/aws/aws-sdk-go: ""
      only:
        # This repository contains other go.sum file used for testing.
        # So we want to be sure that we only update the one at the root of the repository
        - path: "go.mod"

Automerge pull request

Opening one pull request per patch version update can generate a burden on teams, so considering how low-risk patch updates are, we want to automatically merge them as long as tests are passing.

Pull request labels

Pull request labels can have a lot of meanings. For example the great tool Release Drafter relies on them to automatically generate release changelog.

Grouping changes

Projects with a lot of dependencies can have a lot of patch version update so considering how low-risk they are, we want to group them together into one pull request.

Different version update type

We only want to update patch version updates and handle minor and major updates with a different update pipeline.

Specific dependency pipeline

A lot of activity happens on github.com/aws/aws-sdk-go which resulting into multiple releases per day. Considering we have good testing in place we prefer to use the Updatecli declarative approach to specifically handle this library update.

Each plugin comes with its parameters and they each allow to opt in/out of behaviors.

Conclusion

Obviously, the Autodiscovery feature created its own set of challenges and highlighted existing ones to a different scale. It’s not always easy to describe in a declarative way how things should automatically be updated. Especially considering each ecosystem’s specificities.

We had to find some workaround around rules we put on ourselves like "Updatecli should be able to work alone without additional tool", well sometimes it is just better to delegate to ecosystem-specific tools.

We learned a lot in the process, and we are now comfortable enough to consider this feature stable enough for our usage. Or at least stable enough that we won’t introduce voluntary breaking changes.

Is the work over?

No, we still have a lot of pipelines we want to improve but at least what we consider the core of Autodiscovery feature, is stable enough that we don’t want to introduce breaking changes. Our goal was to generate pipelines and provide a way to opt in/out, and that’s what we did.

We refrained from introducing more Autodiscovery plugins until we considered the feature mature enough. We are now planning to add more based on our needs.

To go further with the Autodiscovery feature:

What’s coming next?

As we worked on the Autodiscovery, we discovered we needed something between, the declarative approach that provides us a lot of customization but requires us to write and maintain update pipelines manually. And the Autodiscovery, which requires writing and maintaining Golang code.

We started experimenting with the idea of "shared" update policies that could be stored on a registry like the GitHub registry or Dockerhub. This would allow us to leverage Golang templates to write more "advanced" Updatecli policies generically and reuse them across Git repositories.

It’s still an early experimentation that I would like to cover in a different blog post. But meanwhile, you can still have a look to:

As usual, feel free to reach out.

Stay tuned

Cheers,

Top