From 7ce727bc8ad65cc49e07b792302596bebd1d3d67 Mon Sep 17 00:00:00 2001 From: mr-forust Date: Sat, 26 Sep 2026 20:09:13 +0200 Subject: [PATCH] chore(renovate): move config under renovate/ and validate it in CI The config lived in renovate.json at the repo root while everything else Renovate-related sat under renovate/, and renovate/config.js was a second, unused source of truth. Both are gone: renovate/renovate.json is now the only config file. Because the CronJob in the cluster cannot read the repository, its ConfigMap carries an inlined copy of the config. That copy is generated, and sync-renovate-configmap.sh --check now fails the build when it drifts from the source file. The workflows also stop carrying a copy of the renovate/renovate image tag. They read it from renovate/k8s/cronjob.yaml, so the version validated in CI is the version that actually runs in the cluster. ci.yaml validates the config with renovate-config-validator, checks the generated ConfigMap, and kubeconforms the CronJob's own manifests. --- .gitea/workflows/renovate-ci.yaml | 57 ++++--- .gitea/workflows/renovate-run.yaml | 30 +++- .gitea/workflows/sync-renovate-configmap.sh | 55 +++++++ renovate.json | 60 -------- renovate/README.md | 35 ++++- renovate/config.js | 44 ------ renovate/k8s/configmap.yaml | 160 +++++++++++++++----- renovate/k8s/cronjob.yaml | 6 +- renovate/renovate-compose.yaml | 8 +- renovate/renovate.json | 128 ++++++++++++++++ 10 files changed, 410 insertions(+), 173 deletions(-) create mode 100755 .gitea/workflows/sync-renovate-configmap.sh delete mode 100644 renovate.json delete mode 100644 renovate/config.js create mode 100644 renovate/renovate.json diff --git a/.gitea/workflows/renovate-ci.yaml b/.gitea/workflows/renovate-ci.yaml index a0b1375..60c29d1 100644 --- a/.gitea/workflows/renovate-ci.yaml +++ b/.gitea/workflows/renovate-ci.yaml @@ -10,30 +10,52 @@ on: jobs: validate-renovate: runs-on: [self-hosted, linux, arch, homelab] + timeout-minutes: 20 steps: - name: Checkout repository uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 - - name: Validate Renovate Compose draft + # renovate/k8s/cronjob.yaml is the single source of truth for the image tag, + # so the same version that runs in the cluster is the one validated here. + - name: Resolve the deployed Renovate image + id: image shell: bash run: | set -euo pipefail - trap 'rm -f renovate/.env' EXIT - printf '%s\n' \ - 'RENOVATE_ENDPOINT=https://gitea.example/api/v1' \ - 'RENOVATE_TOKEN=test-token' \ - 'RENOVATE_REPOSITORIES=forust/homelab' \ - > renovate/.env - docker compose -f renovate/renovate-compose.yaml config --quiet + image="$(sed -n 's|.*image:[[:space:]]*\(renovate/renovate:[^[:space:]]*\).*|\1|p' \ + renovate/k8s/cronjob.yaml | head -1)" + if [ -z "$image" ]; then + echo "::error::no renovate/renovate image found in renovate/k8s/cronjob.yaml" + exit 1 + fi + echo "using $image" + echo "image=$image" >> "$GITHUB_OUTPUT" - - name: Validate Kubernetes manifests + - name: Validate Renovate repository config shell: bash run: | set -euo pipefail docker run --rm \ - -v "$PWD:/work" \ - -w /work \ - ghcr.io/yannh/kubeconform:latest \ + -v "$PWD/renovate:/opt/renovate:ro" \ + -e RENOVATE_CONFIG_FILE=/opt/renovate/renovate.json \ + "${{ steps.image.outputs.image }}" \ + renovate-config-validator /opt/renovate/renovate.json + + # The CronJob cannot read the repository, so renovate/k8s/configmap.yaml + # carries an inlined copy of the config. Fail if it no longer matches. + - name: Check the generated Renovate ConfigMap + shell: bash + run: | + set -euo pipefail + ./.gitea/workflows/sync-renovate-configmap.sh --check + + - name: Validate Renovate Kubernetes manifests + shell: bash + run: | + set -euo pipefail + tools_dir="$(bash .gitea/workflows/install-ci-tools.sh kubeconform)" + export PATH="$tools_dir:$PATH" + kubeconform \ -strict \ -ignore-missing-schemas \ -summary \ @@ -41,12 +63,11 @@ jobs: renovate/k8s/configmap.yaml \ renovate/k8s/cronjob.yaml - - name: Validate Renovate repository config + - name: Validate Renovate Compose file shell: bash run: | set -euo pipefail - docker run --rm \ - -v "$PWD:/work" \ - -w /work \ - renovate/renovate:44.103.0 \ - renovate-config-validator renovate.json + source .gitea/workflows/compose-lint.sh + mapfile -t safe_flags < <(compose_safe_flags) + validate_compose_file renovate/renovate-compose.yaml \ + ${safe_flags[@]+"${safe_flags[@]}"} diff --git a/.gitea/workflows/renovate-run.yaml b/.gitea/workflows/renovate-run.yaml index c9888a0..b5faca9 100644 --- a/.gitea/workflows/renovate-run.yaml +++ b/.gitea/workflows/renovate-run.yaml @@ -28,18 +28,36 @@ concurrency: jobs: run-renovate: runs-on: [self-hosted, linux, arch, homelab] + timeout-minutes: 60 steps: - name: Checkout repository uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + # renovate/k8s/cronjob.yaml is the single source of truth for the image tag. + # Reading it here means this workflow validates and runs the exact version + # that is deployed, instead of a copy that silently goes stale. + - name: Resolve the deployed Renovate image + id: image + shell: bash + run: | + set -euo pipefail + image="$(sed -n 's|.*image:[[:space:]]*\(renovate/renovate:[^[:space:]]*\).*|\1|p' \ + renovate/k8s/cronjob.yaml | head -1)" + if [ -z "$image" ]; then + echo "::error::no renovate/renovate image found in renovate/k8s/cronjob.yaml" + exit 1 + fi + echo "using $image" + echo "image=$image" >> "$GITHUB_OUTPUT" + - name: Validate Renovate config shell: bash run: | set -euo pipefail docker run --rm \ - -v "$PWD/renovate/config.js:/opt/renovate/config.js:ro" \ - -e RENOVATE_CONFIG_FILE=/opt/renovate/config.js \ - renovate/renovate:44.103.0 \ + -v "$PWD/renovate/renovate.json:/opt/renovate/renovate.json:ro" \ + -e RENOVATE_CONFIG_FILE=/opt/renovate/renovate.json \ + "${{ steps.image.outputs.image }}" \ renovate-config-validator - name: Run Renovate @@ -56,14 +74,14 @@ jobs: : "${RENOVATE_TOKEN:?missing RENOVATE_TOKEN secret — add a renovate-bot PAT in repo/org Actions secrets}" docker run --rm \ - -v "$PWD/renovate/config.js:/opt/renovate/config.js:ro" \ + -v "$PWD/renovate/renovate.json:/opt/renovate/renovate.json:ro" \ -e RENOVATE_PLATFORM=gitea \ -e RENOVATE_ENDPOINT=https://gitea.forust.xyz/api/v1 \ -e RENOVATE_TOKEN="$RENOVATE_TOKEN" \ -e RENOVATE_GITHUB_COM_TOKEN="${RENOVATE_GITHUB_COM_TOKEN:-}" \ -e RENOVATE_REPOSITORIES="${RENOVATE_REPOSITORIES:-forust/homelab}" \ -e RENOVATE_DRY_RUN="${RENOVATE_DRY_RUN:-}" \ - -e RENOVATE_CONFIG_FILE=/opt/renovate/config.js \ + -e RENOVATE_CONFIG_FILE=/opt/renovate/renovate.json \ -e RENOVATE_BASE_DIR=/tmp/renovate \ -e LOG_LEVEL="${LOG_LEVEL:-info}" \ - renovate/renovate:44.103.0 + "${{ steps.image.outputs.image }}" diff --git a/.gitea/workflows/sync-renovate-configmap.sh b/.gitea/workflows/sync-renovate-configmap.sh new file mode 100755 index 0000000..ce04c62 --- /dev/null +++ b/.gitea/workflows/sync-renovate-configmap.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +# Regenerates renovate/k8s/configmap.yaml from renovate/renovate.json. +# +# renovate/renovate.json is the single source of truth: the CronJob, the Compose +# file and the renovate-run workflow all mount that exact file. A ConfigMap cannot +# read a file from the repository, so the same bytes are inlined here as a literal +# block. This script keeps the copy honest: +# +# .gitea/workflows/sync-renovate-configmap.sh # rewrite in place +# .gitea/workflows/sync-renovate-configmap.sh --check # fail if out of date +# +# renovate-ci runs the --check form on every PR and push, so a config change that +# forgets to regenerate the ConfigMap cannot be merged. +set -euo pipefail + +here="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +repo="$(git -C "$here" rev-parse --show-toplevel)" + +src="$repo/renovate/renovate.json" +dst="$repo/renovate/k8s/configmap.yaml" +[ -f "$src" ] || { + echo "missing $src" >&2 + exit 1 +} + +render() { + cat <<'HEADER' +# GENERATED FILE - do not edit by hand. +# Source: renovate/renovate.json +# Regenerate: .gitea/workflows/sync-renovate-configmap.sh +# Verify: .gitea/workflows/sync-renovate-configmap.sh --check +apiVersion: v1 +kind: ConfigMap +metadata: + name: renovate-config + namespace: renovate +data: + renovate.json: | +HEADER + sed 's/^/ /' "$src" +} + +if [ "${1:-}" = "--check" ]; then + if ! diff -u "$dst" <(render) >/dev/null 2>&1; then + echo "ERROR: $dst is out of sync with renovate/renovate.json" + echo "Run: .gitea/workflows/sync-renovate-configmap.sh" + diff -u "$dst" <(render) || true + exit 1 + fi + echo "renovate/k8s/configmap.yaml is in sync with renovate/renovate.json" + exit 0 +fi + +render >"$dst" +echo "wrote $dst" diff --git a/renovate.json b/renovate.json deleted file mode 100644 index b548672..0000000 --- a/renovate.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": ["config:recommended"], - "enabledManagers": ["dockerfile", "docker-compose", "kubernetes", "helm-values", "custom.regex"], - "helm-values": { - "managerFilePatterns": ["/k8s/.+values\\.ya?ml$/"] - }, - "kubernetes": { - "managerFilePatterns": ["/k8s/.+\\.ya?ml$/"] - }, - "customManagers": [ - { - "customType": "regex", - "description": "singlesource: playwright npm version pinned in npx command (k8s + compose)", - "fileMatch": ["^edu_master/k8s/playwright\\.yaml$", "^edu_master/compose\\.yaml$"], - "matchStrings": ["playwright@(?\\d+\\.\\d+\\.\\d+)"], - "datasourceTemplate": "npm", - "depNameTemplate": "playwright" - }, - { - "customType": "regex", - "description": "singlesource: PLAYWRIGHT_VERSION file", - "fileMatch": ["^edu_master/PLAYWRIGHT_VERSION$"], - "matchStrings": ["^(?\\d+\\.\\d+\\.\\d+)$"], - "datasourceTemplate": "pypi", - "depNameTemplate": "playwright" - } - ], - "packageRules": [ - { - "description": "singlesource playwright - use whichever version is found, keep docker+pypi+npm in sync", - "matchPackageNames": ["playwright", "mcr.microsoft.com/playwright"], - "groupName": "playwright singlesource", - "groupSlug": "playwright" - }, - { - "description": "playwright must not automerge - version skew breaks WS handshake (checker.py:1523 vs playwright.yaml:20)", - "matchPackageNames": ["playwright", "mcr.microsoft.com/playwright"], - "automerge": false - }, - { - "description": "Keep private homelab images unchanged", - "matchDatasources": ["docker"], - "matchPackageNames": ["/gcr\\.forust\\.xyz\\/forust\\/.+/"], - "enabled": false - }, - { - "description": "Require approval for major upgrades", - "matchUpdateTypes": ["major"], - "dependencyDashboardApproval": true, - "automerge": false - }, - { - "description": "Group container patch updates", - "matchDatasources": ["docker"], - "matchUpdateTypes": ["patch"], - "groupName": "container patch updates" - } - ] -} diff --git a/renovate/README.md b/renovate/README.md index bc84aa2..99039b6 100644 --- a/renovate/README.md +++ b/renovate/README.md @@ -26,15 +26,17 @@ from Git and must be applied separately after every new cluster. Run it immediately instead of waiting for the six-hour schedule. -Two options, both use the same `renovate/config.js`: +Two options, both use the same `renovate/renovate.json`: ```sh kubectl create job --from=cronjob/renovate renovate-manual-$(date +%s) -n renovate ``` or the `renovate-run` Actions workflow (Actions tab → `renovate-run` → -Run workflow). It runs `renovate/renovate:44.103.0` on the self-hosted -runner via Docker. Required Actions secrets (repo or org settings): +Run workflow). It runs the same image as the CronJob on the self-hosted runner +via Docker — the tag is read out of `renovate/k8s/cronjob.yaml` at run time +rather than hardcoded, so the two cannot drift apart. Required Actions secrets +(repo or org settings): - `RENOVATE_TOKEN` — renovate-bot PAT (repository + issue read/write). - `RENOVATE_GITHUB_COM_TOKEN` — optional, for changelogs and GitHub rate limits. @@ -64,6 +66,33 @@ docker compose -f renovate-compose.yaml run --rm renovate The Compose file is intentionally named `renovate-compose.yaml`, so the repository's automatic deployment discovery does not start it accidentally. +## Configuration + +`renovate/renovate.json` is the single source of truth. The Compose file and the +`renovate-run` workflow mount that file directly. + +A ConfigMap cannot read from the repository, so the CronJob needs the config +inlined. `renovate/k8s/configmap.yaml` is therefore a **generated** copy: + +```sh +.gitea/workflows/sync-renovate-configmap.sh # regenerate after editing +.gitea/workflows/sync-renovate-configmap.sh --check # fail if out of date +``` + +The `renovate-ci` workflow runs the `--check` form on every PR and push, so a +config edit that forgets to regenerate the ConfigMap cannot be merged. + +Beyond images, `customManagers` in the config track: + +- Helm chart versions pinned in `.gitea/workflows/deploy-lib.sh`. The built-in + `helmv3` manager only reads `Chart.yaml` and `helm-values` only reads values + files, so neither sees a version written into a `helm upgrade` command — + these are declared as `custom.regex` managers against the `helm` datasource. +- CI linter versions in `.gitea/workflows/tool-versions.env`. + +The Renovate image tag is deliberately _not_ in `tool-versions.env`: +`renovate/k8s/cronjob.yaml` owns it, and the workflows read it from there. + ## How updates flow Renovate scans both `compose.yaml` files and Kubernetes manifests, opens a diff --git a/renovate/config.js b/renovate/config.js deleted file mode 100644 index 8500f74..0000000 --- a/renovate/config.js +++ /dev/null @@ -1,44 +0,0 @@ -module.exports = { - platform: 'gitea', - endpoint: process.env.RENOVATE_ENDPOINT || 'https://gitea.forust.xyz/api/v1', - enabledManagers: ['docker-compose', 'kubernetes', 'helm-values'], - 'helm-values': { - managerFilePatterns: ['/k8s/.+values\\.ya?ml$/'], - }, - kubernetes: { - managerFilePatterns: ['/k8s/.+\\.ya?ml$/'], - }, - repositories: (process.env.RENOVATE_REPOSITORIES || '') - .split(',') - .map((repository) => repository.trim()) - .filter(Boolean), - onboarding: false, - requireConfig: 'optional', - autodiscover: false, - dependencyDashboard: true, - prCreation: 'immediate', - labels: ['dependencies', 'automated'], - extends: [ - 'config:recommended', - ':dependencyDashboard', - ], - packageRules: [ - { - description: 'Do not update private homelab images', - matchDatasources: ['docker'], - matchPackageNames: ['/gcr\\.forust\\.xyz\\/forust\\/.+/'], - enabled: false, - }, - { - description: 'Keep major upgrades manual', - matchUpdateTypes: ['major'], - dependencyDashboardApproval: true, - automerge: false, - }, - { - description: 'Group patch updates', - matchUpdateTypes: ['patch'], - groupName: 'container patch updates', - }, - ], -}; diff --git a/renovate/k8s/configmap.yaml b/renovate/k8s/configmap.yaml index bbba610..9a9964c 100644 --- a/renovate/k8s/configmap.yaml +++ b/renovate/k8s/configmap.yaml @@ -1,51 +1,139 @@ +# GENERATED FILE - do not edit by hand. +# Source: renovate/renovate.json +# Regenerate: .gitea/workflows/sync-renovate-configmap.sh +# Verify: .gitea/workflows/sync-renovate-configmap.sh --check apiVersion: v1 kind: ConfigMap metadata: name: renovate-config namespace: renovate data: - config.js: | - module.exports = { - platform: 'gitea', - endpoint: process.env.RENOVATE_ENDPOINT || 'https://gitea.forust.xyz/api/v1', - enabledManagers: ['docker-compose', 'kubernetes', 'helm-values'], - 'helm-values': { - managerFilePatterns: ['/k8s/.+values\\.ya?ml$/'], + renovate.json: | + { + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended", ":dependencyDashboard"], + "enabledManagers": ["dockerfile", "docker-compose", "kubernetes", "helm-values", "custom.regex"], + "onboarding": false, + "requireConfig": "optional", + "autodiscover": false, + "dependencyDashboard": true, + "prCreation": "immediate", + "labels": ["dependencies", "automated"], + "helm-values": { + "managerFilePatterns": ["/k8s/.+values\\.ya?ml$/"] }, - kubernetes: { - managerFilePatterns: ['/k8s/.+\\.ya?ml$/'], + "kubernetes": { + "managerFilePatterns": ["/k8s/.+\\.ya?ml$/"] }, - repositories: (process.env.RENOVATE_REPOSITORIES || '') - .split(',') - .map((repository) => repository.trim()) - .filter(Boolean), - onboarding: false, - requireConfig: 'optional', - autodiscover: false, - dependencyDashboard: true, - prCreation: 'immediate', - labels: ['dependencies', 'automated'], - extends: [ - 'config:recommended', - ':dependencyDashboard', + "customManagers": [ + { + "customType": "regex", + "description": "singlesource: playwright npm version pinned in npx command (k8s + compose)", + "managerFilePatterns": ["^edu_master/k8s/playwright\\.yaml$", "^edu_master/compose\\.yaml$"], + "matchStrings": ["playwright@(?\\d+\\.\\d+\\.\\d+)"], + "datasourceTemplate": "npm", + "depNameTemplate": "playwright" + }, + { + "customType": "regex", + "description": "singlesource: PLAYWRIGHT_VERSION file", + "managerFilePatterns": ["^edu_master/PLAYWRIGHT_VERSION$"], + "matchStrings": ["^(?\\d+\\.\\d+\\.\\d+)$"], + "datasourceTemplate": "pypi", + "depNameTemplate": "playwright" + }, + { + "customType": "regex", + "description": "kube-prometheus-stack chart version pinned in the deploy workflow", + "managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"], + "matchStrings": ["\\|prometheus-community/kube-prometheus-stack\\|prometheus\\|(?[0-9.]+)\\|"], + "datasourceTemplate": "helm", + "depNameTemplate": "kube-prometheus-stack", + "registryUrlTemplate": "https://prometheus-community.github.io/helm-charts" + }, + { + "customType": "regex", + "description": "grafana/loki chart version pinned in the deploy workflow", + "managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"], + "matchStrings": ["\\|grafana/loki\\|prometheus\\|(?[0-9.]+)\\|"], + "datasourceTemplate": "helm", + "depNameTemplate": "loki", + "registryUrlTemplate": "https://grafana.github.io/helm-charts" + }, + { + "customType": "regex", + "description": "grafana/alloy chart version pinned in the deploy workflow", + "managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"], + "matchStrings": ["\\|grafana/alloy\\|prometheus\\|(?[0-9.]+)\\|"], + "datasourceTemplate": "helm", + "depNameTemplate": "alloy", + "registryUrlTemplate": "https://grafana.github.io/helm-charts" + }, + { + "customType": "regex", + "description": "actionlint version used by the ci workflow", + "managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"], + "matchStrings": ["(?:^|\\n)ACTIONLINT_VERSION=\"(?[0-9.]+)\""], + "datasourceTemplate": "github-tags", + "depNameTemplate": "rhysd/actionlint" + }, + { + "customType": "regex", + "description": "shellcheck version used by the ci workflow", + "managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"], + "matchStrings": ["(?:^|\\n)SHELLCHECK_VERSION=\"(?[0-9.]+)\""], + "datasourceTemplate": "github-tags", + "depNameTemplate": "koalaman/shellcheck" + }, + { + "customType": "regex", + "description": "kubeconform version used by the ci workflow", + "managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"], + "matchStrings": ["(?:^|\\n)KUBECONFORM_VERSION=\"(?[0-9.]+)\""], + "datasourceTemplate": "github-tags", + "depNameTemplate": "yannh/kubeconform" + } ], - packageRules: [ + "packageRules": [ { - description: 'Do not update private homelab images', - matchDatasources: ['docker'], - matchPackageNames: ['/gcr\\.forust\\.xyz\\/forust\\/.+/'], - enabled: false, + "description": "Keep private homelab images unchanged", + "matchDatasources": ["docker"], + "matchPackageNames": ["/gcr\\.forust\\.xyz\\/forust\\/.+/"], + "enabled": false }, { - description: 'Keep major upgrades manual', - matchUpdateTypes: ['major'], - dependencyDashboardApproval: true, - automerge: false, + "description": "singlesource playwright - use whichever version is found, keep docker+pypi+npm in sync", + "matchPackageNames": ["playwright", "mcr.microsoft.com/playwright"], + "groupName": "playwright singlesource", + "groupSlug": "playwright" }, { - description: 'Group patch updates', - matchUpdateTypes: ['patch'], - groupName: 'container patch updates', + "description": "playwright must not automerge - version skew breaks the WS handshake (checker.py:1523 vs playwright.yaml:20)", + "matchPackageNames": ["playwright", "mcr.microsoft.com/playwright"], + "automerge": false }, - ], - }; + { + "description": "Renovate updates itself in lockstep across the CronJob and the Compose file", + "matchPackageNames": ["renovate/renovate"], + "groupName": "renovate self-update", + "automerge": false + }, + { + "description": "Helm chart bumps change PVC fields and admission behaviour, keep them reviewable", + "matchDatasources": ["helm"], + "automerge": false + }, + { + "description": "Require approval for major upgrades", + "matchUpdateTypes": ["major"], + "dependencyDashboardApproval": true, + "automerge": false + }, + { + "description": "Group container patch updates", + "matchDatasources": ["docker"], + "matchUpdateTypes": ["patch"], + "groupName": "container patch updates" + } + ] + } diff --git a/renovate/k8s/cronjob.yaml b/renovate/k8s/cronjob.yaml index 8e66405..cbb228d 100644 --- a/renovate/k8s/cronjob.yaml +++ b/renovate/k8s/cronjob.yaml @@ -36,7 +36,7 @@ spec: name: renovate-secrets key: RENOVATE_REPOSITORIES - name: RENOVATE_CONFIG_FILE - value: /opt/renovate/config.js + value: /opt/renovate/renovate.json - name: RENOVATE_BASE_DIR value: /tmp/renovate - name: RENOVATE_GITHUB_COM_TOKEN @@ -49,8 +49,8 @@ spec: value: info volumeMounts: - name: config - mountPath: /opt/renovate/config.js - subPath: config.js + mountPath: /opt/renovate/renovate.json + subPath: renovate.json readOnly: true volumes: - name: config diff --git a/renovate/renovate-compose.yaml b/renovate/renovate-compose.yaml index cbc859c..0d63628 100644 --- a/renovate/renovate-compose.yaml +++ b/renovate/renovate-compose.yaml @@ -1,6 +1,8 @@ services: renovate: - image: renovate/renovate:44.103.0 + # Kept in step with renovate/k8s/cronjob.yaml by the "renovate self-update" + # package rule in renovate/renovate.json. + image: renovate/renovate:44.115.9 container_name: renovate restart: "no" env_file: @@ -10,8 +12,8 @@ services: RENOVATE_ENDPOINT: ${RENOVATE_ENDPOINT:?set RENOVATE_ENDPOINT} RENOVATE_TOKEN: ${RENOVATE_TOKEN:?set RENOVATE_TOKEN} RENOVATE_REPOSITORIES: ${RENOVATE_REPOSITORIES:?set RENOVATE_REPOSITORIES} - RENOVATE_CONFIG_FILE: /opt/renovate/config.js + RENOVATE_CONFIG_FILE: /opt/renovate/renovate.json RENOVATE_BASE_DIR: /tmp/renovate LOG_LEVEL: ${LOG_LEVEL:-info} volumes: - - ./config.js:/opt/renovate/config.js:ro + - ./renovate.json:/opt/renovate/renovate.json:ro diff --git a/renovate/renovate.json b/renovate/renovate.json new file mode 100644 index 0000000..1caa141 --- /dev/null +++ b/renovate/renovate.json @@ -0,0 +1,128 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended", ":dependencyDashboard"], + "enabledManagers": ["dockerfile", "docker-compose", "kubernetes", "helm-values", "custom.regex"], + "onboarding": false, + "requireConfig": "optional", + "autodiscover": false, + "dependencyDashboard": true, + "prCreation": "immediate", + "labels": ["dependencies", "automated"], + "helm-values": { + "managerFilePatterns": ["/k8s/.+values\\.ya?ml$/"] + }, + "kubernetes": { + "managerFilePatterns": ["/k8s/.+\\.ya?ml$/"] + }, + "customManagers": [ + { + "customType": "regex", + "description": "singlesource: playwright npm version pinned in npx command (k8s + compose)", + "managerFilePatterns": ["^edu_master/k8s/playwright\\.yaml$", "^edu_master/compose\\.yaml$"], + "matchStrings": ["playwright@(?\\d+\\.\\d+\\.\\d+)"], + "datasourceTemplate": "npm", + "depNameTemplate": "playwright" + }, + { + "customType": "regex", + "description": "singlesource: PLAYWRIGHT_VERSION file", + "managerFilePatterns": ["^edu_master/PLAYWRIGHT_VERSION$"], + "matchStrings": ["^(?\\d+\\.\\d+\\.\\d+)$"], + "datasourceTemplate": "pypi", + "depNameTemplate": "playwright" + }, + { + "customType": "regex", + "description": "kube-prometheus-stack chart version pinned in the deploy workflow", + "managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"], + "matchStrings": ["\\|prometheus-community/kube-prometheus-stack\\|prometheus\\|(?[0-9.]+)\\|"], + "datasourceTemplate": "helm", + "depNameTemplate": "kube-prometheus-stack", + "registryUrlTemplate": "https://prometheus-community.github.io/helm-charts" + }, + { + "customType": "regex", + "description": "grafana/loki chart version pinned in the deploy workflow", + "managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"], + "matchStrings": ["\\|grafana/loki\\|prometheus\\|(?[0-9.]+)\\|"], + "datasourceTemplate": "helm", + "depNameTemplate": "loki", + "registryUrlTemplate": "https://grafana.github.io/helm-charts" + }, + { + "customType": "regex", + "description": "grafana/alloy chart version pinned in the deploy workflow", + "managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"], + "matchStrings": ["\\|grafana/alloy\\|prometheus\\|(?[0-9.]+)\\|"], + "datasourceTemplate": "helm", + "depNameTemplate": "alloy", + "registryUrlTemplate": "https://grafana.github.io/helm-charts" + }, + { + "customType": "regex", + "description": "actionlint version used by the ci workflow", + "managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"], + "matchStrings": ["(?:^|\\n)ACTIONLINT_VERSION=\"(?[0-9.]+)\""], + "datasourceTemplate": "github-tags", + "depNameTemplate": "rhysd/actionlint" + }, + { + "customType": "regex", + "description": "shellcheck version used by the ci workflow", + "managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"], + "matchStrings": ["(?:^|\\n)SHELLCHECK_VERSION=\"(?[0-9.]+)\""], + "datasourceTemplate": "github-tags", + "depNameTemplate": "koalaman/shellcheck" + }, + { + "customType": "regex", + "description": "kubeconform version used by the ci workflow", + "managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"], + "matchStrings": ["(?:^|\\n)KUBECONFORM_VERSION=\"(?[0-9.]+)\""], + "datasourceTemplate": "github-tags", + "depNameTemplate": "yannh/kubeconform" + } + ], + "packageRules": [ + { + "description": "Keep private homelab images unchanged", + "matchDatasources": ["docker"], + "matchPackageNames": ["/gcr\\.forust\\.xyz\\/forust\\/.+/"], + "enabled": false + }, + { + "description": "singlesource playwright - use whichever version is found, keep docker+pypi+npm in sync", + "matchPackageNames": ["playwright", "mcr.microsoft.com/playwright"], + "groupName": "playwright singlesource", + "groupSlug": "playwright" + }, + { + "description": "playwright must not automerge - version skew breaks the WS handshake (checker.py:1523 vs playwright.yaml:20)", + "matchPackageNames": ["playwright", "mcr.microsoft.com/playwright"], + "automerge": false + }, + { + "description": "Renovate updates itself in lockstep across the CronJob and the Compose file", + "matchPackageNames": ["renovate/renovate"], + "groupName": "renovate self-update", + "automerge": false + }, + { + "description": "Helm chart bumps change PVC fields and admission behaviour, keep them reviewable", + "matchDatasources": ["helm"], + "automerge": false + }, + { + "description": "Require approval for major upgrades", + "matchUpdateTypes": ["major"], + "dependencyDashboardApproval": true, + "automerge": false + }, + { + "description": "Group container patch updates", + "matchDatasources": ["docker"], + "matchUpdateTypes": ["patch"], + "groupName": "container patch updates" + } + ] +}