feat(renovate): add Gitea update automation

Run Renovate in Kubernetes to create reviewed image update PRs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
forustandCopilot committed 2026-09-14 09:16:01 +02:00
1 parent 6715f9e9af
commit 7288058df6
11 files changed
+331

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
name: renovate-ci
on:
pull_request:
push:
branches:
- main
workflow_dispatch:
jobs:
validate-renovate:
runs-on: [self-hosted, linux, arch, homelab]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Validate Renovate Compose draft
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
- name: Validate Kubernetes manifests
shell: bash
run: |
set -euo pipefail
for manifest in renovate/k8s/namespace.yaml renovate/k8s/configmap.yaml renovate/k8s/cronjob.yaml; do
kubectl apply --dry-run=client --validate=false -f "$manifest" >/dev/null
done
- name: Validate Renovate repository config
shell: bash
run: |
set -euo pipefail
docker run --rm \
-v "$PWD:/work" \
-w /work \
renovate/renovate:44.83.2 \
renovate-config-validator renovate.json
+45
View File
@@ -0,0 +1,45 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"enabledManagers": [
"docker-compose",
"kubernetes"
],
"kubernetes": {
"managerFilePatterns": [
"/k8s/.+\\.ya?ml$/"
]
},
"packageRules": [
{
"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"
}
]
}
+4
View File
@@ -0,0 +1,4 @@
RENOVATE_ENDPOINT=https://gitea.forust.xyz/api/v1
RENOVATE_TOKEN=
RENOVATE_REPOSITORIES=forust/homelab
LOG_LEVEL=info
+59
View File
@@ -0,0 +1,59 @@
# Renovate for Gitea
Renovate runs as a Kubernetes CronJob and creates container image update pull
requests in Gitea. It does not deploy changes itself.
## Kubernetes
Create a dedicated Gitea user named `renovate-bot`, create a repository access
token, and grant it repository read/write plus issue read/write permissions.
Add `read:packages` if Renovate must inspect private Gitea registry images.
Create the ignored Secret locally; never commit the PAT:
```sh
cp renovate/k8s/secrets.yaml.example renovate/k8s/secrets.yaml
$EDITOR renovate/k8s/secrets.yaml
kubectl apply -f renovate/k8s/namespace.yaml
kubectl apply -f renovate/k8s/secrets.yaml
kubectl apply -f renovate/k8s/configmap.yaml
kubectl apply -f renovate/k8s/cronjob.yaml
```
The `renovate/k8s/active` marker makes the normal deployment workflow include
the namespace, ConfigMap, and CronJob. The Secret is intentionally excluded
from Git and must be applied separately after every new cluster.
Run it immediately instead of waiting for the six-hour schedule:
```sh
kubectl create job --from=cronjob/renovate renovate-manual-$(date +%s) -n renovate
```
Inspect runs with:
```sh
kubectl get cronjob,jobs,pods -n renovate
kubectl logs -n renovate job/<job-name>
```
`RENOVATE_GITHUB_COM_TOKEN` is optional but recommended for changelogs and
GitHub API rate limits. Set it in the Kubernetes Secret if available.
## Compose
Copy `.env.example` to `.env`, set the PAT, and run:
```sh
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.
## How updates flow
Renovate scans both `compose.yaml` files and Kubernetes manifests, opens a
branch and PR with image tag changes, and waits for CI. After merge, the
existing deployment workflow applies Kubernetes changes or redeploys Compose
stacks. Renovate never updates running workloads directly.
+41
View File
@@ -0,0 +1,41 @@
module.exports = {
platform: 'gitea',
endpoint: process.env.RENOVATE_ENDPOINT,
enabledManagers: ['docker-compose', 'kubernetes'],
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',
},
],
};
View File
Whitespace-only changes.
+45
View File
@@ -0,0 +1,45 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: renovate-config
namespace: renovate
data:
config.js: |
module.exports = {
platform: 'gitea',
endpoint: process.env.RENOVATE_ENDPOINT,
enabledManagers: ['docker-compose', 'kubernetes'],
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',
},
],
};
+58
View File
@@ -0,0 +1,58 @@
apiVersion: batch/v1
kind: CronJob
metadata:
name: renovate
namespace: renovate
spec:
schedule: "17 */6 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 2
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 1
template:
spec:
restartPolicy: Never
containers:
- name: renovate
image: renovate/renovate:44.83.2
env:
- name: RENOVATE_PLATFORM
value: gitea
- name: RENOVATE_ENDPOINT
valueFrom:
secretKeyRef:
name: renovate-secrets
key: RENOVATE_ENDPOINT
- name: RENOVATE_TOKEN
valueFrom:
secretKeyRef:
name: renovate-secrets
key: RENOVATE_TOKEN
- name: RENOVATE_REPOSITORIES
valueFrom:
secretKeyRef:
name: renovate-secrets
key: RENOVATE_REPOSITORIES
- name: RENOVATE_CONFIG_FILE
value: /opt/renovate/config.js
- name: RENOVATE_BASE_DIR
value: /tmp/renovate
- name: RENOVATE_GITHUB_COM_TOKEN
valueFrom:
secretKeyRef:
name: renovate-secrets
key: RENOVATE_GITHUB_COM_TOKEN
optional: true
- name: LOG_LEVEL
value: info
volumeMounts:
- name: config
mountPath: /opt/renovate/config.js
subPath: config.js
readOnly: true
volumes:
- name: config
configMap:
name: renovate-config
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: renovate
labels:
app.kubernetes.io/part-of: renovate
+11
View File
@@ -0,0 +1,11 @@
apiVersion: v1
kind: Secret
metadata:
name: renovate-secrets
namespace: renovate
type: Opaque
stringData:
RENOVATE_TOKEN: ""
RENOVATE_ENDPOINT: "https://gitea.forust.xyz/api/v1"
RENOVATE_REPOSITORIES: "forust/homelab"
RENOVATE_GITHUB_COM_TOKEN: ""
+17
View File
@@ -0,0 +1,17 @@
services:
renovate:
image: renovate/renovate:44.83.2
container_name: renovate
restart: "no"
env_file:
- .env
environment:
RENOVATE_PLATFORM: gitea
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_BASE_DIR: /tmp/renovate
LOG_LEVEL: ${LOG_LEVEL:-info}
volumes:
- ./config.js:/opt/renovate/config.js:ro