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:
1 parent
6715f9e9af
commit
7288058df6
11 files changed
+331
No files matched your search
@@ -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
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
RENOVATE_ENDPOINT=https://gitea.forust.xyz/api/v1
|
||||||
|
RENOVATE_TOKEN=
|
||||||
|
RENOVATE_REPOSITORIES=forust/homelab
|
||||||
|
LOG_LEVEL=info
|
||||||
@@ -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.
|
||||||
@@ -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',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
Whitespace-only changes.
@@ -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',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: renovate
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/part-of: renovate
|
||||||
@@ -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: ""
|
||||||
@@ -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
|
||||||
Reference in new issue
Block a user