diff --git a/kubernetes/code/renovate/config.json b/kubernetes/code/renovate/config.json new file mode 100644 index 0000000..c3cf93e --- /dev/null +++ b/kubernetes/code/renovate/config.json @@ -0,0 +1,11 @@ +{ + "platform": "github", + "repositories": ["vhaudiquet/homeprod"], + "autodiscover": false, + "allowedPostUpgradeCommands": ["^sops"], + "allowedPostUpgradeCommandsEnv": ["^SOPS_"], + "onboardingConfigFileName": "renovate.json", + "dryRun": false, + "binarySource": "docker", + "schedule": ["every weekend"] +} diff --git a/kubernetes/code/renovate/cronjob.yaml b/kubernetes/code/renovate/cronjob.yaml new file mode 100644 index 0000000..3c038b7 --- /dev/null +++ b/kubernetes/code/renovate/cronjob.yaml @@ -0,0 +1,71 @@ +# Self-hosted Renovate runner. +# +# Runs the managed `renovatebot/renovate` image on a schedule against the +# `vhaudiquet/homeprod` repo (see config.json). Because we need to run SOPS +# during postUpgradeTasks (re-encrypting values.yaml), Renovate cannot run on +# the Mend-hosted app — it must be self-hosted with the sops command allow-listed +# (see config.json -> allowedPostUpgradeCommands) and the SOPS PGP private key +# loaded (renovate-gpg secret below). +apiVersion: batch/v1 +kind: CronJob +metadata: + name: renovate + namespace: renovate +spec: + # Run every hour; Renovate's own "schedule": ["every weekend"] (config.json) + # gates when PRs are actually created. + schedule: "42 * * * *" + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 2 + failedJobsHistoryLimit: 4 + jobTemplate: + spec: + backoffLimit: 0 + template: + spec: + restartPolicy: Never + tolerations: + - key: CriticalAddonsOnly + operator: Exists + containers: + - name: renovate + image: renovatebot/renovate:latest + envFrom: + - secretRef: + name: renovate-secrets + env: + - name: RENOVATE_CONFIG_FILE + value: /etc/renovate/config.json + - name: LOG_LEVEL + value: info + command: + - /bin/sh + - -ec + - | + set -eu + # Import the SOPS PGP private key so rebuild/enable SOPS + # re-encryption in postUpgradeTasks works. + gpg --batch --import /etc/renovate/gpg/git-renovate-gpg.key || true + exec renovate + volumeMounts: + - name: config + mountPath: /etc/renovate/config.json + subPath: config.json + readOnly: true + - name: gpg + mountPath: /etc/renovate/gpg + readOnly: true + resources: + requests: + cpu: 100m + memory: 384Mi + limits: + cpu: 1000m + memory: 1Gi + volumes: + - name: config + configMap: + name: renovate-config + - name: gpg + secret: + name: renovate-gpg diff --git a/kubernetes/code/renovate/git-renovate-gpg.key b/kubernetes/code/renovate/git-renovate-gpg.key new file mode 100644 index 0000000..918860b --- /dev/null +++ b/kubernetes/code/renovate/git-renovate-gpg.key @@ -0,0 +1,9 @@ +# SOPS PGP PRIVATE key for fingerprint DC6910268E657FF70BA7EC289974494E76938DDC +# +# PLACEHOLDER — replace with the actual ASCII-armored PRIVATE key used to +# encrypt .sops.yaml files, then sops-encrypt this file before committing +# (wrap the whole file as a single value matching the `.*key` regex in +# .sops.yaml, or store it via the .pre-commit flow). Keep it out of git in +# plaintext; this is a Secret source. +# +# -----BEGIN PGP PRIVATE KEY BLOCK----- ... -----END PGP PRIVATE KEY BLOCK----- diff --git a/kubernetes/code/renovate/kustomization.yaml b/kubernetes/code/renovate/kustomization.yaml new file mode 100644 index 0000000..5c91c96 --- /dev/null +++ b/kubernetes/code/renovate/kustomization.yaml @@ -0,0 +1,22 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: renovate +resources: + - namespace.yaml + - cronjob.yaml +# Scalar Renovate env vars (token, SOPS key fingerprint, ...). Stored as a +# dotenv file and SOPS-encrypted by the .pre-commit hook (matches .env rule). +configMapGenerator: + - name: renovate-config + files: + - config.json=config.json +secretGenerator: + - name: renovate-secrets + envs: + - renovate.env + # SOPS PGP private key needed by the renovate runner to re-encrypt + # values.yaml during postUpgradeTasks. Mounted into the pod and imported + # into the container gpg keyring at startup. + - name: renovate-gpg + files: + - git-renovate-gpg.key diff --git a/kubernetes/code/renovate/namespace.yaml b/kubernetes/code/renovate/namespace.yaml new file mode 100644 index 0000000..ec7c378 --- /dev/null +++ b/kubernetes/code/renovate/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: renovate diff --git a/kubernetes/code/renovate/renovate.env b/kubernetes/code/renovate/renovate.env new file mode 100644 index 0000000..98a8660 --- /dev/null +++ b/kubernetes/code/renovate/renovate.env @@ -0,0 +1,18 @@ +# Self-hosted Renovate runtime secrets. +# +# FILL THESE IN. This file is SOPS-encrypted on commit by the .pre-commit hook +# (it matches the `.env` rule in .sops.yaml), and decrypted by kustomize-controller +# via flux-sops before the Secret is generated. + +# Fine-grained/personal access token with read+writes:content and read:issues, +# read:pull_requests, and contents read/write on vhaudiquet/homeprod so Renovate +# can open and push PR branches. +RENOVATE_TOKEN= + +# Optional: GitHub token used for public API rate-limit boosting (can be the +# same as RENOVATE_TOKEN, or a dedicated one). +GITHUB_COM_TOKEN= + +# SOPS master key fingerprint used to (re-)encrypt values.yaml in this repo. +# Matches the `pgp:` entry in .sops.yaml. +SOPS_PGP_FP=DC6910268E657FF70BA7EC289974494E76938DDC diff --git a/kubernetes/kustomization.yaml b/kubernetes/kustomization.yaml index 26968ad..7abc7c0 100644 --- a/kubernetes/kustomization.yaml +++ b/kubernetes/kustomization.yaml @@ -25,6 +25,8 @@ resources: - infrastructure/traefik - infrastructure/caddy - infrastructure/network/blocky + # Code (self-hosted dev tooling) + - code/renovate # Dev (developer platform) - dev/gitea - dev/harbor diff --git a/renovate.json b/renovate.json index 90270df..6a8682b 100644 --- a/renovate.json +++ b/renovate.json @@ -9,6 +9,16 @@ "helm-values", "helmv3" ], + "postUpgradeTasks": { + "description": "SOPS round-trip on encrypted values.yaml. Renovate has no preUpgrade hook, so the decrypt must happen here: after the tag bump but before commit, decrypt the file, then re-encrypt. Encrypting an already-encrypted file would double-encrypt the existing ENC secret values and corrupt them.", + "commands": [ + "sops -d {{packageFile}} > {{packageFile}}.plain && mv {{packageFile}}.plain {{packageFile}} && sops -e -i {{packageFile}}" + ], + "fileFilters": [ + "**/values.yaml" + ], + "executionMode": "update" + }, "packageRules": [ { "description": "docker-compose updates",