From dcf03c2226966423993a8db0bc822ad130b299c5 Mon Sep 17 00:00:00 2001 From: Oleh Martsokha Date: Fri, 3 Jul 2026 04:19:41 +0200 Subject: [PATCH] Switch dependency automation from Dependabot to Renovate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dependabot bumps `uv.lock` but has no hook to regenerate downstream artifacts (our per-service `requirements.txt` files). Every Python bump PR therefore failed CI's `gen_requirements.py --check` step and needed a manual follow-up commit. Renovate's `postUpgradeTasks` runs a whitelisted command after each dependency bump and commits the resulting file changes onto the same PR, so branches arrive already green. - `renovate.json`: replaces the three dependabot entries with three Renovate `packageRules` (cargo, pep621, github-actions) keeping the existing group names, commit prefixes, and weekly Monday cadence. `postUpgradeTasks` runs `uv sync --all-packages && uv run python scripts/gen_requirements.py` after each bump and commits any changes to `packages/*/requirements.txt` or `uv.lock`. - `.github/workflows/renovate.yml`: self-hosted runner (hourly cron + manual dispatch) using the `renovatebot/github-action` container. `postUpgradeTasks` requires self-hosted; the free hosted Mend app doesn't run arbitrary commands. Uses a GitHub App token so opened PRs trigger our other workflows (Rs Build / Py Build) — the built-in GITHUB_TOKEN doesn't chain workflow triggers. - `RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS` env var pins the runner to the exact regen command. Anything else in `renovate.json` is silently ignored — the security backstop against a compromised config running arbitrary code. - `.github/dependabot.yml`: deleted. ## Setup step (not code) Before merging: create a GitHub App with `contents: write` and `pull-requests: write` permissions, install it on the repo, and add `RENOVATE_APP_ID` + `RENOVATE_APP_PRIVATE_KEY` as repo secrets. Without these, the workflow will fail at the `Get token` step. Detailed setup: https://github.com/renovatebot/github-action#usage-with-a-github-app --- .github/dependabot.yml | 66 ---------------------------------- .github/workflows/renovate.yml | 61 +++++++++++++++++++++++++++++++ renovate.json | 43 ++++++++++++++++++++++ 3 files changed, 104 insertions(+), 66 deletions(-) delete mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/renovate.yml create mode 100644 renovate.json diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 734ad9fa..00000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,66 +0,0 @@ -version: 2 -updates: - # Rust: cargo workspace at repo root - - package-ecosystem: "cargo" - directory: "/" - schedule: - interval: "weekly" - timezone: "Europe/Berlin" - day: "monday" - time: "04:00" - open-pull-requests-limit: 5 - labels: - - "chore" - commit-message: - prefix: "chore(deps)" - prefix-development: "chore(deps-dev)" - rebase-strategy: "auto" - versioning-strategy: "auto" - groups: - rust-dependencies: - patterns: - - "*" - update-types: - - "minor" - - "patch" - - # Python: uv workspace at repo root - - package-ecosystem: "uv" - directory: "/" - schedule: - interval: "weekly" - timezone: "Europe/Berlin" - day: "monday" - time: "04:00" - open-pull-requests-limit: 5 - labels: - - "chore" - commit-message: - prefix: "chore(deps-py)" - prefix-development: "chore(deps-py-dev)" - rebase-strategy: "auto" - groups: - python-dependencies: - patterns: - - "*" - update-types: - - "minor" - - "patch" - - # GitHub Actions - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "weekly" - timezone: "Europe/Berlin" - day: "monday" - time: "04:00" - open-pull-requests-limit: 5 - labels: - - "chore" - commit-message: - prefix: "chore(actions)" - groups: - github-actions: - patterns: - - "*" diff --git a/.github/workflows/renovate.yml b/.github/workflows/renovate.yml new file mode 100644 index 00000000..3ee4b8e4 --- /dev/null +++ b/.github/workflows/renovate.yml @@ -0,0 +1,61 @@ +name: Renovate + +on: + schedule: + # Every hour on the hour; Renovate itself decides which PRs to open based + # on `renovate.json` schedule + prConcurrentLimit. + - cron: "0 * * * *" + workflow_dispatch: + inputs: + logLevel: + description: "Renovate log level" + required: false + default: "info" + type: choice + options: + - debug + - info + - warn + +concurrency: + group: renovate-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + renovate: + name: Renovate + runs-on: ubuntu-latest + + steps: + - name: Get token + id: token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ secrets.RENOVATE_APP_ID }} + private-key: ${{ secrets.RENOVATE_APP_PRIVATE_KEY }} + + - name: Checkout + uses: actions/checkout@v7 + with: + token: ${{ steps.token.outputs.token }} + + # `postUpgradeTasks.commands` needs uv on PATH; install it before running + # Renovate so post-upgrade `uv sync && gen_requirements.py` succeeds. + - name: Install uv + uses: astral-sh/setup-uv@v7 + + - name: Renovate + uses: renovatebot/github-action@v46.1.17 + env: + # Whitelist the exact command postUpgradeTasks may run. Anything not + # matching this regex is silently ignored, which is the security + # backstop against a compromised renovate.json running arbitrary code. + RENOVATE_ALLOWED_POST_UPGRADE_COMMANDS: '["^uv sync --all-packages && uv run python scripts/gen_requirements\\.py$"]' + RENOVATE_ALLOW_POST_UPGRADE_COMMAND_TEMPLATING: "true" + LOG_LEVEL: ${{ inputs.logLevel || 'info' }} + with: + token: ${{ steps.token.outputs.token }} + configurationFile: renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000..e723b8e6 --- /dev/null +++ b/renovate.json @@ -0,0 +1,43 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended", + ":semanticCommits", + ":semanticCommitTypeAll(chore)" + ], + "timezone": "Europe/Berlin", + "schedule": ["before 05:00 on monday"], + "prConcurrentLimit": 5, + "prHourlyLimit": 0, + "labels": ["chore"], + "rebaseWhen": "conflicted", + "rangeStrategy": "auto", + "commitMessagePrefix": "chore(deps):", + "packageRules": [ + { + "matchManagers": ["cargo"], + "groupName": "rust-dependencies", + "matchUpdateTypes": ["minor", "patch"], + "commitMessagePrefix": "chore(deps):" + }, + { + "matchManagers": ["pep621"], + "groupName": "python-dependencies", + "matchUpdateTypes": ["minor", "patch"], + "commitMessagePrefix": "chore(deps-py):" + }, + { + "matchManagers": ["github-actions"], + "groupName": "github-actions", + "commitMessagePrefix": "chore(actions):" + } + ], + "postUpgradeTasks": { + "commands": ["uv sync --all-packages && uv run python scripts/gen_requirements.py"], + "fileFilters": [ + "packages/*/requirements.txt", + "uv.lock" + ], + "executionMode": "branch" + } +}