Skip to content

feat: integrate Renovate for automated dependency updates - #197

Merged
matrixise merged 4 commits into
masterfrom
feat/integrate-renovatebot
Sep 25, 2026
Merged

matrixise merged 4 commits into
masterfrom
feat/integrate-renovatebot

Conversation

@matrixise

Copy link
Copy Markdown
Contributor

Overview

This PR integrates Renovate for automated dependency management, closing #187.

Following the migration to uv, Renovate will automate dependency updates while maintaining our current requirements/*.in → requirements/*.txt pip-compile workflow.

🔒 Configuration Highlights

Heroku-Safe Deployment Strategy

  • ✅ Explicit file patterns: Only matches main.txt, dev.txt, production.txt
  • ✅ Python 3.13 constraint: Matches our Heroku runtime
  • ✅ Smart automerge: Only dev dependencies patches, never production
  • ✅ Manual review required: All main.txt and production.txt updates need approval

Intelligent Grouping & Scheduling

  • 🐍 Django/Wagtail ecosystem: Grouped together, Monday mornings
  • 🔐 Security updates: Separate PRs with priority 10
  • 📦 Minor/patch updates: Grouped by type, Monday mornings
  • 🔄 Lock file maintenance: Monthly refresh, first Monday

Rate Limiting

  • Max 3 concurrent PRs
  • Max 2 PRs per hour
  • Prevents PR spam

Disabled Conflicting Managers

"pip_requirements": { "enabled": false },
"pip_setup": { "enabled": false }

Per Renovate documentation, this prevents duplicate runs.

📋 What Happens Next?

  1. ✅ Merge this PR with the renovate.json configuration
  2. 🤖 Renovate will create an "onboarding PR" automatically within minutes
  3. 📝 Review the onboarding PR to verify it detected all dependencies correctly
  4. ✅ Merge the onboarding PR to activate automated updates
  5. 🎉 Enjoy automated dependency management!

🔗 References

✨ Benefits

  • 🔐 Security: Automated vulnerability alerts with fix PRs
  • ⏱️ Time savings: No more manual task dependencies:upgrade
  • 🎯 Granular control: Group related updates, schedule by day
  • 👁️ Visibility: Track all dependency updates in one place

🤖 Generated with Claude Code

@matrixise

Copy link
Copy Markdown
Contributor Author

Hi @ulgens

Could you comment/review this PR ?

Thank you

@matrixise
matrixise force-pushed the feat/integrate-renovatebot branch from 17c0049 to f80efba Compare September 25, 2026 05:07
@matrixise

Copy link
Copy Markdown
Contributor Author

Rebased on master and rewrote renovate.json for the new pyproject.toml + uv.lock setup (#247). The old config pointed the pip-compile manager at requirements/*.txt, which no longer exist, so it did nothing.

What changed

  • Manager: removed the pip-compile / pip_requirements / pip_setup blocks. The pep621 manager (enabled by config:recommended) finds pyproject.toml, reads uv.lock and runs uv lock to update it.
  • Main vs dev split: now uses matchDepTypes instead of file paths:
    • [project.dependencies] → depType project.dependencies: never automerged.
    • [dependency-groups] dev → depType dependency-groups: patch updates are automerged.
    • Limitation: pep621 stores the PEP 735 group name only in managerData.depGroup, so there is no way to match the dev group by name. That's fine while dev is the only group. If we add another group later, this rule needs another look.
    • Dev patches get their own group (Dev dependencies (patch)). Renovate only automerges a branch when every upgrade in it allows automerge, so sharing a branch with production deps would block it.
  • Security fix: the vulnerability settings sat in a packageRule limited to matchUpdateTypes: ["patch"] and to versions that don't start with 0.. That skipped fixes that need a minor bump (for example djangorestframework 3.17→3.18 and soupsieve 2.8→2.10). They now live in the top-level vulnerabilityAlerts object, with no limit on update type. I removed prPriority: the validator doesn't allow it there, and vulnerability PRs already skip schedule and the PR limits.
  • "All non-major dependencies" now has matchManagers: ["pep621"], so Dockerfile and GitHub Actions bumps stay out of the Monday Python group.
  • Removed options: matchPackagePatterns, excludePackagePatterns and matchFiles were removed from Renovate. They're replaced by matchPackageNames regexes (/^django/i, !/^wagtail/i, …) and depTypes.
  • requires-python updates are turned off (Python stays on 3.13.x).

⚠️ Important finding: most dependencies are not tracked individually

A local renovate --platform=local --dry-run=extract run shows that 30 of the 35 dependencies are skipped with skipReason: unspecified-version. pep621 ignores any dependency without a version specifier in pyproject.toml ("boto3", "pandas", …). Only these get their own update PRs: django<6.1, wagtail>=7.3.2,<7.4, requests>=2.32.5 and ruff==0.15.16.

As a result, the uv.lock refresh (lockFileMaintenance) is the only way the other dependencies get updated. I changed it from monthly to weekly (Monday before 9am). The same limit applies to security fixes for unpinned or transitive packages: Renovate can only fix them through that lock refresh. For better coverage, we could add lower bounds in pyproject.toml (for example "boto3>=1.43"). I left that out of this PR.

Validation

  • renovate-config-validator --strict --no-global renovate.json (latest Renovate): ✅
  • Local extraction: depTypes and depGroup: "dev" confirmed on the real pyproject.toml.
  • Renovate reads comments in .json files (JSONC), but the docs recommend a .jsonc file for that, so I put the explanations in each rule's description field.

Reminder: vulnerabilityAlerts requires the Dependency graph and Dependabot alerts to be enabled on the repo, and the Renovate app needs read access to Dependabot alerts.

Not merging. Ready for review.

matrixise and others added 2 commits September 25, 2026 07:34
Add Renovate configuration with pip-compile support for automated
dependency management. Configuration includes:

- Explicit file patterns for main.txt, dev.txt, production.txt
- Python 3.13 constraint matching Heroku runtime
- Django/Wagtail ecosystem grouping with Monday scheduling
- Intelligent automerge: only dev.txt patches, never production
- Security updates prioritized with separate PRs
- Rate limiting to avoid PR spam
- Monthly lock file maintenance

This setup is optimized for safe Heroku deployments with manual
review required for production dependencies.

Closes #187

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
The previous config was written for the pip-compile workflow and pointed
at requirements/{main,dev,production}.txt, which no longer exist since
the migration to pyproject.toml + uv.lock (#247). It was a no-op.

- Drop the pip-compile / pip_requirements / pip_setup blocks; the pep621
  manager (enabled by config:recommended) picks up pyproject.toml and
  drives `uv lock` for uv.lock.
- Main vs dev split now uses depTypes instead of file paths:
  [project.dependencies] -> "project.dependencies" (never automerged),
  [dependency-groups] -> "dependency-groups" (patch automerge). pep621
  keeps the PEP 735 group name only in managerData, so it cannot be
  matched; "dev" is currently the only group. Dev patches get their own
  group so automerge is not blocked by production deps on the same branch.
- Security: the vulnerability alert settings were inside a packageRule
  restricted to patch updates (and to non-0.x versions), which excluded
  fixes needing a minor bump. Move them to the top-level
  vulnerabilityAlerts object with no update-type restriction. prPriority
  is dropped: it is not allowed there, and vulnerability PRs already
  bypass schedule and PR limits.
- Restrict the "All non-major dependencies" group to pep621 so Dockerfile
  and GitHub Actions bumps are not mixed into the Monday Python group.
- Replace the removed matchPackagePatterns / excludePackagePatterns /
  matchFiles options with matchPackageNames regexes and depTypes.
- Disable requires-python updates: Python is pinned to 3.13.x.
- Run lockFileMaintenance weekly instead of monthly: most dependencies in
  pyproject.toml have no version specifier, so Renovate skips them
  (skipReason "unspecified-version") and the uv.lock refresh is the only
  way they get updated.

Validated with `renovate-config-validator --strict --no-global` and a
local `renovate --platform=local --dry-run=extract` run.
pep621 has no manager-specific range strategy, so "auto" resolves to
"replace". With "replace", a ">=" lower bound that already allows the
new version produces no update at all, which would make the lower
bounds added to pyproject.toml useless.

"bump" raises the lower bound in pyproject.toml and updates uv.lock in
the same PR. Checked with a local renovate dry-run on a copy with older
locked versions: "replace" proposed nothing, while "bump" proposed
boto3 >=1.43.90 -> >=1.43.102, requests >=2.33.0 -> >=2.34.2, etc.
@matrixise

Copy link
Copy Markdown
Contributor Author

Added rangeStrategy: "bump" for pep621. This goes with #248, which adds a >= lower bound (the currently locked version) to every dependency in pyproject.toml.

Why both are needed: a local Renovate dry-run on a copy with older locked versions (boto3, requests, coverage) showed:

  • default strategy (replace): no update at all, because >=X already allows the new version;
  • bump: boto3>=1.43.90 → >=1.43.102 in pyproject.toml, plus uv.lock.

With the final config, the simulated branches are renovate/all-non-major-dependencies (boto3, requests, coverage, ruff), renovate/django-ecosystem (django 6.1, wagtail 7.4) and renovate/major-django-ecosystem (wagtail 8.0).

Note: ruff is pinned on purpose (0.16 changes the default rule set), but Renovate will still propose 0.16.x in the non-major group. Close that PR or add an ignore rule if that's not wanted.

Merge #197 and #248 together: either one alone doesn't change what Renovate does.

- uv is versioned in three places (pep621 dev dependency "uv", Docker
  image "ghcr.io/astral-sh/uv", mise tool "astral-sh/uv"): group them in
  a single "uv" PR so they stay in sync.
- Python is pinned to 3.13.x: the Docker base image, mise and
  .python-version only accept 3.13 (requires-python is already
  disabled).
- Production runs PostgreSQL 17 on Heroku: CI and docker-compose stay on
  17.

Checked with a local renovate dry-run across all managers: the Dockerfile
and mise uv bumps land on renovate/uv, and no Python 3.14 or Postgres 18
update is proposed.
@matrixise

Copy link
Copy Markdown
Contributor Author

Added three rules (ff55026 → d32f152):

  • uv grouped: the uv dev dependency, the ghcr.io/astral-sh/uv image in the Dockerfile and the astral-sh/uv tool in mise.toml now come in a single renovate/uv PR.
  • Python 3.13 only for the Docker base image, mise.toml and .python-version.
  • Postgres 17 only for CI and docker-compose, to match production.

A local dry-run with all managers confirms the grouping, and no Python 3.14 or Postgres 18 PR is proposed. The one remaining major is redis:6.2 → 8.x in docker-compose.yml. It will open as its own PR, so we can decide there whether to follow it (depends on the Redis Cloud version used in production).

The ruff pin is no longer an issue: #248 moves to ruff 0.16.9 and fixes the new lint findings.

@matrixise
matrixise merged commit 8de79a2 into master Sep 25, 2026
2 checks passed
@matrixise
matrixise deleted the feat/integrate-renovatebot branch September 25, 2026 06:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant