Skip to content

Audit: Renovate for dependency bumps

What we check

  • .github/workflows/renovate.yml exists -- runs renovate hourly on a self-hosted runner. RENOVATE_AUTODISCOVER_FILTER is ${{ github.repository }}, supplied by the runtime, so the file is copied verbatim rather than edited per repository.
  • renovate.yml does not run renovatebot/github-action, which needs a Docker daemon on the runner.
  • renovate.json exists, with package grouping rules and scheduling. It is a per-repository adaptation, not expected to be byte-identical to the template's copy.
  • renovate.json extends customManagers:githubActionsVersions, so the Renovate version pinned in renovate.yml is bumped.
  • renovate.json enables the pre-commit manager, where the repository has remote pre-commit hooks to manage.

Renovate without Docker

renovatebot/github-action runs Renovate as a container. The static runner pool is gaining ephemeral pods on a Kubernetes cluster, and they have no Docker daemon, so the action fails on every run a pod takes. The template now sets up Node and runs a pinned Renovate from npm, which works on a VM and a pod alike. A workflow step that still uses the action is a finding. The action named in a comment is not.

The template pins Renovate behind a # renovate: comment, which only the built-in customManagers:githubActionsVersions preset reads. A copy of the new workflow without the preset in renovate.json runs a Renovate that never bumps itself, so that is a finding too. regexManagers:githubActionsVersions, the preset's name before Renovate renamed it, also passes.

Both findings go in the one issue, so a repository still on the old copy is told to change both files at once. The template README covers what else changes when Renovate leaves its container. The one that matters is lock files, which Renovate now refreshes with the runner's own tools. The template provides npm always and a Rust toolchain when the repository has a Cargo.lock, so every repository can copy it unmodified.

The pre-commit manager

Renovate's pre-commit manager is opt-in: cargo, dockerfile, github-actions and the Python managers are on by default, but .pre-commit-config.yaml is not read at all unless the config says so. A repository can therefore look fully renovate-managed while its hook revisions age untouched. That matters more than the usual stale dependency: pre-commit hooks are the linters gating every commit, so an unwatched pin means the thing judging everything else is itself unjudged. instar was four months behind on actionlint while its cargo, dockerfile and github-actions dependencies were current.

Any of renovate's three enabling forms passes:

{"pre-commit": {"enabled": true}}
{"enabledManagers": ["pre-commit", "..."]}
{"extends": [":enablePreCommit"]}

The check applies only when there is something to bump: a repository with no .pre-commit-config.yaml, or one whose hooks are all repo: local, passes without the manager. sfui's "pre-commit": {"enabled": true} is the form the template carries.

Enabling the manager is not enough for the shellcheck hook: shellcheck-py's four-part tags need the template's regex versioning rule, or the lookup fails on the dependency dashboard and the hook is never bumped. This check does not look for that rule; see the template README.

Repositories that ship workflow templates

Renovate's github-actions manager only reads .github/workflows, .github/actions and workflow-templates. A repository that keeps workflows anywhere else -- development ships the fleet's templates from templates/ -- gets its own workflows bumped while the copies it hands out stay behind, so every repository onboarded after a bump starts life a version stale.

Extending the manager's file patterns brings them into view. The patterns extend the defaults rather than replacing them, so only the extra path is named:

{
  "github-actions": {
    "managerFilePatterns": ["/^templates/.+\\.ya?ml$/"]
  }
}

This applies to development today, and to any repository that grows a second home for workflow files.

customManagers:githubActionsVersions has the same blind spot, and its file patterns cannot be extended from outside the preset. So development also carries a customManagers entry with the preset's match string and templates/ as its only file pattern. That keeps the template's Renovate pin moving with the copy the repository runs.

Python version constraints

Projects supporting multiple Linux distributions set constraints.python to the oldest Python they support, so renovate stops proposing updates the oldest distribution cannot install:

{"constraints": {"python": ">=3.8"}}

Currently required for: agent-python, occystrap.

The value matches requires-python in pyproject.toml -- both derive from the system Python of the oldest supported distribution. Where a project has a supported platforms matrix that table lives in ARCHITECTURE.md, and both files carry a comment pointing back to it, so dropping a distribution means three edits. CI should test on the oldest supported Python, so a bump that breaks it fails there rather than on a user's machine.

Package grouping and range strategy

Tightly coupled dependencies (the grpc stack, for instance) are grouped so they bump together:

{
  "packageRules": [
    {
      "description": "Group grpc packages together",
      "matchPackagePatterns": [
        "^grpcio",
        "^googleapis-common-protos",
        "^protobuf"
      ],
      "groupName": "grpc packages"
    }
  ]
}

Where a family is released in lockstep rather than merely coupled -- the oslo libraries cut coordinated releases, so four pull requests arrive the same evening for one upstream event -- grouping stops being a preference and becomes an audited requirement. See renovate-lockstep-groups.md, which is the criterion that measures it; this section stays the general advice.

Server projects (shakenfist, kerbside) pin exactly (==) and use renovate's default range strategy, which bumps those pins on every release. That is right for software running on infrastructure we control.

Client and library projects (agent-python, client-python, client-python-k3s, clingwrap, occystrap) constrain loosely (>=) so they install across a wide range of distributions and Python versions. For those the grpc group adds "rangeStrategy": "widen", so renovate only opens a pull request when a new major version falls outside the existing range.

Without it renovate raises the floor of every >= constraint on every minor release, which is churn -- and worse than churn on the newest distributions. Fedora 43 ships Python 3.14, and older grpcio releases have no wheels for it; a loose constraint lets pip choose a version that does, while a raised floor or an exact pin sends it to a source build that fails wherever a C++ compiler is missing. Nothing is given up by staying loose: the gRPC wire protocol is stable across minor versions, and proto3 serialization is stable within a major version.

Template

Template: templates/renovate/ See: templates/renovate/README.md

Projects

Per-project compliance for this criterion is regenerated on every run of the consistency audit: see the compliance page.

📝 Report an issue with this page