Compare commits

...
Author SHA1 Message Date
forust 5f9354b9a8 fix(edu): deploy reviewed application release with Redis authentication
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 14s
ci / lint-actionlint (push) Successful in 6s
ci / lint-shellcheck (push) Successful in 13s
ci / lint-prettier (push) Successful in 19s
ci / lint-ruff (push) Successful in 9s
ci / lint-yaml (push) Successful in 10s
ci / lint-dockerfiles (push) Successful in 6s
ci / validate (push) Successful in 10s
ci / build (push) Successful in 30s
2026-10-06 22:56:02 +02:00
forust fcd16f6128 Merge pull request 'chore(deps): update renovate/renovate docker tag to v44.140.0' (#96) from renovate/renovate-self-update into main
renovate-ci / validate-renovate (push) Successful in 1m25s
ci / lint-compose (push) Successful in 12s
ci / lint-actionlint (push) Successful in 7s
ci / lint-shellcheck (push) Successful in 16s
ci / lint-prettier (push) Successful in 23s
ci / lint-ruff (push) Successful in 9s
ci / lint-yaml (push) Successful in 11s
ci / lint-dockerfiles (push) Successful in 7s
ci / validate (push) Successful in 6s
ci / build (push) Successful in 25s
Reviewed-on: #96
2026-10-06 17:51:40 +00:00
renovate-bot 2ac2a94bb4 chore(deps): update renovate/renovate docker tag to v44.140.0 2026-10-06 17:51:40 +00:00
forust 3c7e358dd5 Merge pull request 'chore(deps): update lscr.io/linuxserver/qbittorrent docker tag to v20' (#97) from renovate/lscr.io-linuxserver-qbittorrent-20.x into main
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 16s
ci / lint-actionlint (push) Successful in 7s
ci / lint-shellcheck (push) Successful in 15s
ci / lint-prettier (push) Successful in 20s
ci / lint-ruff (push) Successful in 9s
ci / lint-yaml (push) Successful in 14s
ci / lint-dockerfiles (push) Successful in 7s
ci / validate (push) Successful in 8s
ci / build (push) Successful in 24s
Reviewed-on: #97
2026-10-06 17:51:25 +00:00
renovate-bot 9be8fb6c69 chore(deps): update lscr.io/linuxserver/qbittorrent docker tag to v20 2026-10-06 17:51:25 +00:00
forust 7fdeacffb8 feat(monitoring): stand down Prometheus server during VM trial
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 13s
ci / lint-actionlint (push) Successful in 5s
ci / lint-shellcheck (push) Successful in 10s
ci / lint-prettier (push) Successful in 14s
ci / lint-ruff (push) Successful in 8s
ci / lint-yaml (push) Successful in 10s
ci / lint-dockerfiles (push) Successful in 6s
ci / validate (push) Successful in 7s
ci / build (push) Successful in 19s
vmagent scrapes and remote-writes to VictoriaMetrics, so the Prometheus server scales to 0. Encoded as prometheusSpec.replicas in values instead of a kubectl patch, so helm keeps owning spec.replicas and Helm 4 server-side apply stops conflicting with the kubectl-patch field manager.
2026-10-06 19:29:30 +02:00
forust cef499de73 fix(deploy): skip VMAgent in secrets check before CRD install
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 12s
ci / lint-actionlint (push) Successful in 6s
ci / lint-shellcheck (push) Successful in 15s
ci / lint-ruff (push) Successful in 6s
ci / lint-dockerfiles (push) Successful in 6s
ci / lint-prettier (push) Successful in 21s
ci / lint-yaml (push) Successful in 10s
ci / validate (push) Successful in 8s
ci / build (push) Successful in 21s
check_referenced_secrets ran kubectl create on vmagent.yaml even when the VMAgent CRD is not installed yet, failing validate with 'no matches for kind VMAgent'. Apply the same skip_uninstalled_vmagent_crd guard used by both dry-run loops.
2026-10-06 19:19:47 +02:00
forust 1b70a55300 fix(ci): handle malformed push before SHA
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 10s
ci / lint-actionlint (push) Successful in 7s
ci / lint-shellcheck (push) Successful in 15s
ci / lint-prettier (push) Failing after 22s
ci / lint-ruff (push) Failing after 2s
ci / lint-yaml (push) Failing after 2s
ci / lint-dockerfiles (push) Failing after 3s
ci / validate (push) Failing after 2s
ci / build (push) Skipped
2026-10-06 18:21:19 +02:00
forust 3f2b4e9acf fix(deploy): skip VMAgent preflight before CRD install
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 11s
ci / lint-actionlint (push) Successful in 5s
ci / lint-shellcheck (push) Successful in 10s
ci / lint-prettier (push) Successful in 15s
ci / lint-ruff (push) Successful in 8s
ci / lint-yaml (push) Successful in 10s
ci / lint-dockerfiles (push) Successful in 7s
ci / validate (push) Successful in 10s
ci / build (push) Successful in 25s
2026-10-06 18:18:25 +02:00
forust 6057734a4f fix(renovate): sync generated configmap
renovate-ci / validate-renovate (push) Successful in 9s
ci / lint-compose (push) Successful in 9s
ci / lint-actionlint (push) Successful in 4s
ci / lint-shellcheck (push) Successful in 10s
ci / lint-prettier (push) Successful in 19s
ci / lint-ruff (push) Successful in 8s
ci / lint-yaml (push) Successful in 11s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 7s
ci / build (push) Successful in 18s
2026-10-06 18:08:17 +02:00
forust 8eedf8b74b Merge pull request 'feat(monitoring): add VictoriaMetrics trial stack' (#95) from feat/victoria-metrics-migration into main
ci / lint-compose (push) Successful in 11s
ci / lint-actionlint (push) Successful in 2m36s
ci / lint-shellcheck (push) Successful in 15s
ci / lint-prettier (push) Successful in 19s
ci / lint-ruff (push) Successful in 7s
ci / lint-yaml (push) Successful in 10s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 7s
renovate-ci / validate-renovate (push) Failing after 9s
ci / build (push) Successful in 19s
Reviewed-on: #95
2026-10-06 16:05:46 +00:00
forust 8c0e36a5c0 feat(monitoring): replace scrape dump with vmagent
ci / lint-prettier (push) Skipped
ci / lint-ruff (push) Skipped
ci / lint-yaml (push) Skipped
ci / lint-dockerfiles (push) Skipped
ci / validate (push) Skipped
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (pull_request) Successful in 11s
ci / lint-actionlint (pull_request) Successful in 6s
ci / lint-shellcheck (pull_request) Successful in 16s
ci / lint-dockerfiles (pull_request) Successful in 6s
ci / lint-prettier (pull_request) Successful in 16s
ci / lint-ruff (pull_request) Successful in 7s
ci / lint-yaml (pull_request) Successful in 10s
ci / validate (pull_request) Successful in 7s
ci / build (pull_request) Skipped
renovate-ci / validate-renovate (pull_request) Failing after 1m40s
2026-10-06 18:03:59 +02:00
forust 78cd15f12c chore(monitoring): remove vmctl backfill job
renovate-ci / validate-renovate (pull_request) Skipped
ci / lint-compose (pull_request) Successful in 11s
ci / lint-yaml (pull_request) Successful in 11s
ci / lint-prettier (push) Skipped
ci / lint-ruff (push) Skipped
ci / lint-yaml (push) Skipped
ci / lint-dockerfiles (push) Skipped
ci / validate (push) Skipped
renovate-ci / validate-renovate (push) Skipped
ci / lint-actionlint (pull_request) Successful in 5s
ci / lint-shellcheck (pull_request) Successful in 16s
ci / lint-prettier (pull_request) Successful in 15s
ci / lint-ruff (pull_request) Successful in 6s
ci / lint-dockerfiles (pull_request) Successful in 7s
ci / validate (pull_request) Successful in 7s
ci / build (pull_request) Skipped
One-shot Prometheus history backfill is complete; drop the Job and clean up related comments.
2026-10-06 17:53:24 +02:00
forust 18c633c242 feat(monitoring): add VictoriaMetrics trial stack
ci / lint-prettier (push) Skipped
ci / lint-ruff (push) Skipped
ci / lint-yaml (push) Skipped
ci / lint-dockerfiles (push) Skipped
ci / validate (push) Skipped
renovate-ci / validate-renovate (push) Skipped
renovate-ci / validate-renovate (pull_request) Skipped
ci / lint-compose (pull_request) Successful in 12s
ci / lint-actionlint (pull_request) Successful in 6s
ci / lint-shellcheck (pull_request) Successful in 13s
ci / lint-prettier (pull_request) Successful in 21s
ci / lint-ruff (pull_request) Successful in 7s
ci / lint-yaml (pull_request) Successful in 12s
ci / lint-dockerfiles (pull_request) Successful in 7s
ci / validate (pull_request) Successful in 7s
ci / build (pull_request) Skipped
2026-10-06 17:45:44 +02:00
forust 4510394531 Merge pull request 'chore(deps): update renovate/renovate docker tag to v44.139.0' (#81) from renovate/renovate-self-update into main
ci / lint-compose (push) Successful in 11s
ci / lint-actionlint (push) Successful in 6s
ci / lint-shellcheck (push) Successful in 15s
ci / lint-prettier (push) Successful in 19s
ci / lint-ruff (push) Successful in 7s
ci / lint-yaml (push) Successful in 11s
ci / lint-dockerfiles (push) Successful in 6s
ci / validate (push) Successful in 9s
renovate-ci / validate-renovate (push) Successful in 13s
ci / build (push) Successful in 20s
Reviewed-on: #81
2026-10-06 15:33:45 +00:00
renovate-bot 2545312db1 chore(deps): update renovate/renovate docker tag to v44.139.0 2026-10-06 15:33:45 +00:00
forust 8ce0b809c0 Merge pull request 'chore(deps): update all minor updates' (#77) from renovate/all-minor into main
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Canceled after 0s
ci / lint-actionlint (push) Canceled after 0s
ci / lint-shellcheck (push) Canceled after 0s
ci / lint-prettier (push) Canceled after 0s
ci / lint-ruff (push) Canceled after 0s
ci / lint-yaml (push) Canceled after 0s
ci / lint-dockerfiles (push) Canceled after 0s
ci / validate (push) Canceled after 0s
ci / build (push) Canceled after 0s
Reviewed-on: #77
2026-10-06 15:33:28 +00:00
renovate-bot 506c04e15c chore(deps): update all minor updates 2026-10-06 15:33:28 +00:00
forust bdcbb3d5af Merge pull request 'chore(deps): update all patch updates' (#82) from renovate/all-patch into main
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Canceled after 0s
ci / lint-actionlint (push) Canceled after 0s
ci / lint-shellcheck (push) Canceled after 0s
ci / lint-prettier (push) Canceled after 0s
ci / lint-ruff (push) Canceled after 0s
ci / lint-yaml (push) Canceled after 0s
ci / lint-dockerfiles (push) Canceled after 0s
ci / validate (push) Canceled after 0s
ci / build (push) Canceled after 0s
Reviewed-on: #82
2026-10-06 15:33:09 +00:00
renovate-bot faac6febb8 chore(deps): update all patch updates 2026-10-06 15:33:09 +00:00
forust 695da1308c Merge pull request 'fix(renovate): restore custom extraction and update groups' (#93) from fix/renovate-extraction-groups into main
ci / lint-compose (push) Successful in 10s
ci / lint-actionlint (push) Successful in 6s
ci / lint-shellcheck (push) Successful in 17s
ci / lint-prettier (push) Successful in 18s
ci / lint-ruff (push) Successful in 8s
ci / lint-yaml (push) Successful in 12s
ci / lint-dockerfiles (push) Successful in 8s
ci / validate (push) Successful in 8s
renovate-ci / validate-renovate (push) Successful in 11s
ci / build (push) Successful in 37s
Reviewed-on: #93
2026-10-06 15:31:25 +00:00
forust 552b22cb66 fix(renovate): include self-update Compose filename 2026-10-06 15:31:25 +00:00
forust 3756e60c95 fix(renovate): restore custom extraction and update groups 2026-10-06 15:31:25 +00:00
forust d0d217ddf4 Merge pull request 'fix(deploy): skip verify and smoke when deployment is disabled' (#94) from fix/deploy-skip-when-disabled into main
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Canceled after 0s
ci / lint-actionlint (push) Canceled after 0s
ci / lint-shellcheck (push) Canceled after 0s
ci / lint-prettier (push) Canceled after 0s
ci / lint-ruff (push) Canceled after 0s
ci / lint-yaml (push) Canceled after 0s
ci / lint-dockerfiles (push) Canceled after 0s
ci / validate (push) Canceled after 0s
ci / build (push) Canceled after 0s
Reviewed-on: #94
2026-10-06 15:24:17 +00:00
forust 24f84bab2f fix(deploy): skip verification when deployment is disabled
ci / lint-yaml (push) Skipped
ci / lint-dockerfiles (push) Skipped
ci / validate (push) Skipped
renovate-ci / validate-renovate (push) Skipped
ci / lint-prettier (push) Skipped
ci / lint-ruff (push) Skipped
renovate-ci / validate-renovate (pull_request) Skipped
ci / lint-compose (pull_request) Canceled after 0s
ci / lint-actionlint (pull_request) Canceled after 0s
ci / lint-shellcheck (pull_request) Canceled after 0s
ci / lint-prettier (pull_request) Canceled after 0s
ci / lint-ruff (pull_request) Canceled after 0s
ci / lint-yaml (pull_request) Canceled after 0s
ci / lint-dockerfiles (pull_request) Canceled after 0s
ci / validate (pull_request) Canceled after 0s
ci / build (pull_request) Canceled after 0s
2026-10-06 15:24:06 +00:00
forust 0b8a3c16a7 Merge pull request 'fix(glance): mount CSS from the correct ConfigMap' (#87) from fix/glance-assets into main
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Canceled after 0s
ci / lint-actionlint (push) Canceled after 0s
ci / lint-shellcheck (push) Canceled after 0s
ci / lint-prettier (push) Canceled after 0s
ci / lint-ruff (push) Canceled after 0s
ci / lint-yaml (push) Canceled after 0s
ci / lint-dockerfiles (push) Canceled after 0s
ci / validate (push) Canceled after 0s
ci / build (push) Canceled after 0s
Reviewed-on: #87
2026-10-06 15:17:47 +00:00
forust e9a68aae77 fix(glance): mount CSS from the assets ConfigMap 2026-10-06 15:17:47 +00:00
forust 5359df5ed6 Merge pull request 'fix(postgres): include the required NetBox database password' (#88) from fix/postgres-env-example into main
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 14s
ci / lint-actionlint (push) Successful in 8s
ci / lint-shellcheck (push) Successful in 17s
ci / lint-prettier (push) Successful in 24s
ci / lint-ruff (push) Successful in 9s
ci / lint-yaml (push) Successful in 11s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 11s
ci / build (push) Canceled after 0s
Reviewed-on: #88
2026-10-06 15:17:19 +00:00
forust 8aea0f0d13 fix(postgres): include required NetBox password in Compose env example 2026-10-06 15:17:19 +00:00
forust 65d4f2d482 Merge pull request 'fix(traefik): correct AdGuard and SearXNG Compose rules' (#90) from fix/compose-router-rules into main
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 12s
ci / lint-actionlint (push) Successful in 6s
ci / lint-shellcheck (push) Successful in 13s
ci / lint-prettier (push) Successful in 16s
ci / lint-ruff (push) Successful in 8s
ci / lint-yaml (push) Successful in 11s
ci / lint-dockerfiles (push) Successful in 6s
ci / validate (push) Successful in 10s
ci / build (push) Successful in 23s
Reviewed-on: #90
2026-10-06 15:10:49 +00:00
forust 2fe9b7632f fix(traefik): correct AdGuard and SearXNG Compose router expressions 2026-10-06 15:10:49 +00:00
forust e436d89eef Merge pull request 'fix(netbird): restore Compose setup and runtime renderer' (#86) from fix/netbird-compose-runtime into main
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 11s
ci / lint-actionlint (push) Successful in 4s
ci / lint-shellcheck (push) Successful in 15s
ci / lint-prettier (push) Successful in 21s
ci / lint-ruff (push) Successful in 7s
ci / lint-yaml (push) Successful in 11s
ci / lint-dockerfiles (push) Successful in 6s
ci / validate (push) Successful in 7s
ci / build (push) Successful in 20s
Reviewed-on: #86
2026-10-06 15:10:12 +00:00
forust 8729cb5062 fix(netbird): restore Compose setup and server entrypoint
ci / validate (push) Skipped
ci / lint-prettier (push) Skipped
ci / lint-ruff (push) Skipped
ci / lint-yaml (push) Skipped
ci / lint-dockerfiles (push) Skipped
renovate-ci / validate-renovate (push) Skipped
renovate-ci / validate-renovate (pull_request) Skipped
ci / lint-compose (pull_request) Successful in 12s
ci / lint-actionlint (pull_request) Successful in 8s
ci / lint-shellcheck (pull_request) Successful in 21s
ci / lint-prettier (pull_request) Successful in 17s
ci / lint-ruff (pull_request) Successful in 6s
ci / lint-yaml (pull_request) Successful in 9s
ci / lint-dockerfiles (pull_request) Successful in 5s
ci / validate (pull_request) Successful in 6s
ci / build (pull_request) Skipped
2026-10-06 15:08:46 +00:00
forust f1e4a1088d Merge pull request 'fix(ci): deduplicate PR checks and filter deploy triggers' (#92) from fix/ci-trigger-dedup into main
renovate-ci / validate-renovate (push) Successful in 9s
ci / lint-compose (push) Successful in 10s
ci / lint-actionlint (push) Successful in 6s
ci / lint-shellcheck (push) Successful in 12s
ci / lint-prettier (push) Successful in 19s
ci / lint-ruff (push) Successful in 8s
ci / lint-yaml (push) Successful in 12s
ci / lint-dockerfiles (push) Successful in 7s
ci / validate (push) Successful in 7s
ci / build (push) Successful in 19s
Reviewed-on: #92
2026-10-06 15:06:47 +00:00
forust b357ef95d8 fix(deploy): only create automatic runs for main CI
ci / lint-ruff (push) Skipped
ci / lint-yaml (push) Skipped
ci / lint-dockerfiles (push) Skipped
ci / validate (push) Skipped
ci / lint-prettier (push) Skipped
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (pull_request) Successful in 13s
ci / lint-actionlint (pull_request) Successful in 6s
ci / lint-shellcheck (pull_request) Successful in 11s
ci / lint-prettier (pull_request) Successful in 18s
ci / lint-ruff (pull_request) Successful in 8s
ci / lint-yaml (pull_request) Successful in 13s
ci / lint-dockerfiles (pull_request) Successful in 5s
ci / validate (pull_request) Successful in 9s
ci / build (pull_request) Skipped
renovate-ci / validate-renovate (pull_request) Successful in 12s
2026-10-06 17:00:05 +02:00
forust 67422663b7 fix(ci): avoid duplicate branch and PR runs
ci / validate (push) Skipped
ci / lint-prettier (push) Skipped
ci / lint-ruff (push) Skipped
ci / lint-yaml (push) Skipped
ci / lint-dockerfiles (push) Skipped
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (pull_request) Canceled after 0s
ci / lint-actionlint (pull_request) Canceled after 0s
ci / lint-shellcheck (pull_request) Canceled after 0s
ci / lint-prettier (pull_request) Canceled after 0s
ci / lint-ruff (pull_request) Canceled after 0s
ci / lint-yaml (pull_request) Canceled after 0s
ci / lint-dockerfiles (pull_request) Canceled after 0s
ci / validate (pull_request) Canceled after 0s
ci / build (pull_request) Canceled after 0s
renovate-ci / validate-renovate (pull_request) Successful in 10s
2026-10-06 16:59:26 +02:00
forust 88705fec88 Merge pull request 'fix(deploy): reject unsafe per-file pruning' (#85) from fix/deploy-prune-guard into main
renovate-ci / validate-renovate (push) Successful in 9s
ci / lint-compose (push) Successful in 10s
ci / lint-actionlint (push) Successful in 4s
ci / lint-shellcheck (push) Successful in 11s
ci / lint-prettier (push) Successful in 15s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 10s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 8s
ci / build (push) Successful in 23s
Reviewed-on: #85
2026-10-06 14:57:22 +00:00
forust c098807aa4 fix(deploy): reject destructive per-file pruning before apply
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 14s
ci / lint-actionlint (push) Successful in 8s
ci / lint-shellcheck (push) Successful in 13s
ci / lint-prettier (push) Successful in 19s
ci / lint-ruff (push) Successful in 8s
ci / lint-yaml (push) Successful in 12s
ci / lint-dockerfiles (push) Successful in 8s
ci / validate (push) Successful in 10s
ci / build (push) Skipped
ci / lint-compose (pull_request) Successful in 12s
ci / lint-actionlint (pull_request) Successful in 5s
ci / lint-shellcheck (pull_request) Successful in 11s
ci / lint-prettier (pull_request) Successful in 16s
ci / lint-ruff (pull_request) Successful in 8s
ci / lint-yaml (pull_request) Successful in 11s
ci / lint-dockerfiles (pull_request) Successful in 7s
ci / validate (pull_request) Successful in 7s
ci / build (pull_request) Skipped
renovate-ci / validate-renovate (pull_request) Successful in 10s
2026-10-06 14:57:11 +00:00
forust e1eee2d3c7 Merge pull request 'feat(reloader): enable deploy and integrate configuration reloads' (#91) from fix/reloader-integration into main
ci / lint-compose (push) Canceled after 0s
ci / lint-actionlint (push) Canceled after 0s
ci / lint-shellcheck (push) Canceled after 0s
ci / lint-prettier (push) Canceled after 0s
ci / lint-ruff (push) Canceled after 0s
ci / lint-yaml (push) Canceled after 0s
ci / lint-dockerfiles (push) Canceled after 0s
ci / validate (push) Canceled after 0s
ci / build (push) Canceled after 0s
renovate-ci / validate-renovate (push) Successful in 9s
Reviewed-on: #91
2026-10-06 14:55:16 +00:00
forust ff83daed1e feat(reloader): enable deployment and reload runtime-config consumers
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 13s
ci / lint-actionlint (push) Successful in 9s
ci / lint-shellcheck (push) Successful in 14s
ci / lint-prettier (push) Successful in 16s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 8s
ci / lint-dockerfiles (push) Successful in 5s
ci / validate (push) Successful in 7s
ci / build (push) Skipped
ci / lint-compose (pull_request) Successful in 13s
ci / lint-actionlint (pull_request) Successful in 4s
ci / lint-shellcheck (pull_request) Successful in 12s
ci / lint-prettier (pull_request) Successful in 18s
ci / lint-ruff (pull_request) Successful in 8s
ci / lint-yaml (pull_request) Successful in 13s
ci / lint-dockerfiles (pull_request) Successful in 8s
ci / validate (pull_request) Successful in 7s
ci / build (pull_request) Skipped
renovate-ci / validate-renovate (pull_request) Successful in 9s
2026-10-06 14:54:45 +00:00
forust 080ae343e6 Merge pull request 'fix(deploy): validate resolved Compose config and namespaced Secrets' (#84) from fix/deploy-validation into main
ci / lint-compose (push) Successful in 9s
ci / lint-actionlint (push) Successful in 4s
ci / lint-shellcheck (push) Successful in 11s
ci / lint-prettier (push) Successful in 15s
ci / lint-ruff (push) Successful in 6s
ci / lint-yaml (push) Successful in 12s
ci / lint-dockerfiles (push) Successful in 9s
ci / validate (push) Successful in 11s
renovate-ci / validate-renovate (push) Successful in 10s
ci / build (push) Successful in 29s
Reviewed-on: #84
2026-10-06 14:54:27 +00:00
forust 8d3185f8ab fix(ci): install jq for deploy validation regressions
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 8s
ci / lint-actionlint (push) Successful in 5s
ci / lint-shellcheck (push) Successful in 14s
ci / lint-prettier (push) Successful in 15s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 9s
ci / lint-dockerfiles (push) Successful in 7s
ci / validate (push) Successful in 6s
ci / build (push) Skipped
ci / lint-compose (pull_request) Successful in 11s
ci / lint-actionlint (pull_request) Successful in 7s
ci / lint-shellcheck (pull_request) Successful in 12s
ci / lint-prettier (pull_request) Successful in 14s
ci / lint-ruff (pull_request) Successful in 5s
ci / lint-yaml (pull_request) Successful in 11s
ci / lint-dockerfiles (pull_request) Successful in 6s
ci / validate (pull_request) Successful in 6s
ci / build (pull_request) Skipped
renovate-ci / validate-renovate (pull_request) Successful in 8s
2026-10-06 16:31:48 +02:00
forust ff40a71145 fix(deploy): resolve Compose config and check namespaced pod Secrets
renovate-ci / validate-renovate (push) Skipped
ci / lint-compose (push) Successful in 10s
ci / lint-actionlint (push) Successful in 5s
ci / lint-shellcheck (push) Failing after 10s
ci / lint-prettier (push) Successful in 13s
ci / lint-ruff (push) Successful in 5s
ci / lint-yaml (push) Successful in 11s
ci / lint-dockerfiles (push) Successful in 7s
ci / validate (push) Successful in 8s
ci / build (push) Skipped
ci / lint-compose (pull_request) Canceled after 0s
ci / lint-actionlint (pull_request) Canceled after 0s
ci / lint-shellcheck (pull_request) Canceled after 0s
ci / lint-prettier (pull_request) Canceled after 0s
ci / lint-ruff (pull_request) Canceled after 0s
ci / lint-yaml (pull_request) Canceled after 0s
ci / lint-dockerfiles (pull_request) Canceled after 0s
ci / validate (pull_request) Canceled after 0s
ci / build (pull_request) Canceled after 0s
renovate-ci / validate-renovate (pull_request) Successful in 8s
2026-10-06 15:58:44 +02:00
65 changed files with 1100 additions and 178 deletions

No files matched your search

+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env bash
# Local regressions only: kubectl is mocked and Docker is used for config parsing.
set -euo pipefail
repo="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
scratch="$(mktemp -d)"
trap 'rm -rf "$scratch"' EXIT
mkdir -p "$scratch/repo/app" "$scratch/repo/postgres" "$scratch/repo/netbird" "$scratch/repo/renovate"
git -C "$scratch/repo" init -q
for file in app/compose.yaml postgres/shared-compose.yaml netbird/client.compose.yaml renovate/renovate-compose.yaml; do
touch "$scratch/repo/$file"
done
git -C "$scratch/repo" add .
# shellcheck source=../workflows/compose-lint.sh
source "$repo/.gitea/workflows/compose-lint.sh"
actual="$(cd "$scratch/repo" && compose_files)"
expected=$'app/compose.yaml\nnetbird/client.compose.yaml\npostgres/shared-compose.yaml\nrenovate/renovate-compose.yaml'
[ "$actual" = "$expected" ] || { echo 'Compose discovery missed a file' >&2; exit 1; }
cat >"$scratch/compose.yaml" <<'YAML'
services:
example:
image: busybox:1.37.0
environment:
REQUIRED: ${HOMELAB_TEST_REQUIRED:?required for this regression}
YAML
unset HOMELAB_TEST_REQUIRED
if validate_compose_file "$scratch/compose.yaml" >"$scratch/config.log" 2>&1; then
echo 'Full Compose validation accepted a missing variable' >&2
exit 1
fi
grep -q 'required for this regression' "$scratch/config.log"
HOMELAB_TEST_REQUIRED=present validate_compose_file "$scratch/compose.yaml"
cat >"$scratch/resources.json" <<'JSON'
{"kind":"List","items":[
{"kind":"Deployment","metadata":{"namespace":"app"},"spec":{"template":{"spec":{
"containers":[{"envFrom":[{"secretRef":{"name":"credentials"}},{"secretRef":{"name":"optional","optional":true}}],"env":[{"valueFrom":{"secretKeyRef":{"name":"credentials","key":"password"}}}]}],
"initContainers":[{"envFrom":[{"secretRef":{"name":"init"}}]}],
"imagePullSecrets":[{"name":"registry"}],
"volumes":[{"secret":{"secretName":"mounted"}},{"projected":{"sources":[{"secret":{"name":"projected"}},{"secret":{"name":"optional-projected","optional":true}}]}}]
}}}},
{"kind":"CronJob","metadata":{},"spec":{"jobTemplate":{"spec":{"template":{"spec":{"containers":[{"envFrom":[{"secretRef":{"name":"cron"}}]}]}}}}}},
{"kind":"IngressRoute","metadata":{"namespace":"app"},"spec":{"tls":{"secretName":"controller-issued-tls"}}}
]}
JSON
actual="$(jq -r -f "$repo/.gitea/workflows/secret-references.jq" "$scratch/resources.json" | sort)"
expected=$'app credentials\napp init\napp mounted\napp projected\napp registry\ndefault cron'
[ "$actual" = "$expected" ] || { echo "Unexpected Secret references: $actual" >&2; exit 1; }
REPO="$repo"
# shellcheck source=../workflows/deploy-lib.sh
source "$repo/.gitea/workflows/deploy-lib.sh"
K8S_MANIFESTS=("$scratch/resources.json")
KUSTOMIZE_APPS=()
# No live cluster access. Reject credentials in app even if they exist elsewhere.
kubectl() {
case "$1" in
create) cat "$scratch/resources.json" ;;
get)
if [ "$3" = credentials ] && [ "$5" = app ]; then
return 1
fi
return 0
;;
*) echo "Unexpected kubectl invocation: $*" >&2; return 1 ;;
esac
}
if check_referenced_secrets >"$scratch/secrets.log"; then
echo 'Namespace-scoped Secret check accepted a missing Secret' >&2
exit 1
fi
grep -q 'MISSING OR UNREADABLE: app/credentials' "$scratch/secrets.log"
# API/rendering errors must not produce an empty reference list and pass.
kubectl() { return 1; }
if ! skip_uninstalled_vmagent_crd "$REPO/prometheus-stack/k8s/vmagent.yaml"; then
echo 'VMAgent preflight did not skip an uninstalled CRD' >&2
exit 1
fi
kubectl() { return 0; }
if skip_uninstalled_vmagent_crd "$REPO/prometheus-stack/k8s/vmagent.yaml"; then
echo 'VMAgent preflight skipped an installed CRD' >&2
exit 1
fi
if skip_uninstalled_vmagent_crd "$REPO/prometheus-stack/k8s/victoria.yaml"; then
echo 'VMAgent preflight skipped an unrelated manifest' >&2
exit 1
fi
kubectl() { return 1; }
if check_referenced_secrets >"$scratch/secrets.log"; then
echo 'Secret check accepted a failed manifest render' >&2
exit 1
fi
printf '%s\n' 'Deploy validation regressions passed.'
+26 -34
View File
@@ -3,7 +3,7 @@ name: ci
on:
push:
branches:
- "**"
- main
pull_request:
workflow_dispatch:
@@ -88,7 +88,7 @@ jobs:
shell: bash
run: |
set -euo pipefail
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh shellcheck)"
tools_dir="$(bash .gitea/workflows/install-ci-tools.sh shellcheck jq)"
export PATH="$tools_dir:$PATH"
mapfile -t scripts < <(
git ls-files '*.sh' ':(glob)**/*.bash'
@@ -98,6 +98,7 @@ jobs:
exit 0
fi
shellcheck --external-sources --source-path=SCRIPTDIR --severity=style "${scripts[@]}"
bash .gitea/tests/deploy-validation.sh
lint-prettier:
runs-on: [self-hosted, linux, arch, homelab]
@@ -141,6 +142,7 @@ jobs:
export PATH="$tools_dir:$PATH"
ruff check .
ruff format --check .
python3 -m unittest discover -s tests -v
lint-yaml:
runs-on: [self-hosted, linux, arch, homelab]
@@ -292,6 +294,11 @@ jobs:
echo "server-side dry-run: ${#manifests[@]} manifests, ${#kustomize_apps[@]} kustomize apps"
failed=0
for m in ${manifests[@]+"${manifests[@]}"}; do
if [[ "$m" == "prometheus-stack/k8s/vmagent.yaml" ]] \
&& ! kubectl get crd vmagents.operator.victoriametrics.com >/dev/null 2>&1; then
echo "skip server-side dry-run until the VictoriaMetrics Operator CRD is installed: $m"
continue
fi
if ! out="$(kubectl apply --dry-run=server -f "$m" 2>&1)"; then
failed=1
echo "::error file=${m}::$(printf '%s' "$out" | head -1)"
@@ -331,11 +338,20 @@ jobs:
- name: Detect changed docker-built services
id: services
shell: bash
env:
PUSH_BEFORE: ${{ github.event.before }}
run: |
set -euo pipefail
base="${{ github.event.before }}"
if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ]; then
base="$(git rev-list --max-parents=0 HEAD)"
base="${PUSH_BEFORE:-}"
empty_tree="$(git hash-object -t tree /dev/null)"
if [[ "$base" =~ ^0{40}$ ]]; then
base="$empty_tree"
elif [[ ! "$base" =~ ^[0-9a-fA-F]{40}$ ]] || ! git cat-file -e "${base}^{commit}" 2>/dev/null; then
# Some Gitea push payloads expose `before` as multiple root commits
# joined by newlines. It is not a usable diff base; use this push's
# first parent so image changes in the current commit are still built.
base="$(git rev-parse "${GITHUB_SHA}^" 2>/dev/null || printf '%s' "$empty_tree")"
echo "::warning::invalid push-before value; comparing against ${base}"
fi
# A failed diff used to leave changed_files empty, which reads exactly
@@ -371,9 +387,6 @@ jobs:
homepages/*)
add_service homepages
;;
edu_master/phpsessid-bot/*|edu_master/webinar-checker/*|edu_master/compose.yaml)
add_service edu_master
;;
esac
done
@@ -480,32 +493,6 @@ jobs:
done
done
;;
edu_master)
for variant in session-keeper webinar-checker; do
case "$variant" in
session-keeper)
context="edu_master/phpsessid-bot"
image="${REGISTRY}/forust/session-keeper"
;;
webinar-checker)
context="edu_master/webinar-checker"
image="${REGISTRY}/forust/webinar-checker"
;;
esac
set_tags
build_args=()
for tag in "${tags[@]}"; do
build_args+=(-t "${image}:${tag}")
done
docker build \
--cache-from "type=registry,ref=${image}:buildcache" \
--cache-to "type=registry,ref=${image}:buildcache,mode=max" \
"${build_args[@]}" "$context"
for tag in "${tags[@]}"; do
docker push "${image}:${tag}"
done
done
;;
esac
done
@@ -536,6 +523,11 @@ jobs:
fi
echo "pinning ${#repos[@]} image(s) to $commit_tag"
for repo in "${repos[@]}"; do
# EDU images are released by the application repository and pinned
# directly by digest in edu_master manifests. Never retag them here.
case "$repo" in
*/session-keeper|*/webinar-checker) continue ;;
esac
if docker buildx imagetools inspect "$repo:$commit_tag" >/dev/null 2>&1; then
echo " already built by this push: ${repo##*/}"
continue
+1 -2
View File
@@ -21,8 +21,7 @@
# All committed Compose files, including the ones deploy never starts.
compose_files() {
git ls-files \
'*/compose.yaml' '*/compose.yml' 'compose.yaml' 'compose.yml' \
'*/docker-compose.yaml' '*/docker-compose.yml'
'*compose.yaml' '*compose.yml'
}
# Prints the flags that turn `docker compose config` into the general check.
+75 -44
View File
@@ -31,6 +31,16 @@ warn() {
echo "WARNING: $*" >&2
}
# Prune needs the complete desired set in one invocation. Per-file pruning
# treats resources from the other files as absent and can delete them.
check_prune_mode() {
if [ "$APPLY_PRUNE" = "true" ]; then
echo "ERROR: APPLY_PRUNE=true is unsupported by the per-file deploy loop." >&2
echo "Disable it; remove obsolete resources explicitly after review." >&2
return 1
fi
}
collect_k8s() {
git -C "$REPO" ls-files -- "$1" \
| grep -E '\.ya?ml$' \
@@ -536,6 +546,7 @@ rollback_workloads() {
# have to be declared as custom.regex managers in renovate/renovate.json.
HELM_RELEASES=(
"prometheus-stack|prometheus-community/kube-prometheus-stack|prometheus|86.2.3|prometheus-stack/k8s/grafana-values.yaml|prometheus-stack/k8s/active"
"victoria-operator|victoriametrics/victoria-metrics-operator|prometheus|0.68.1|prometheus-stack/k8s/victoria-operator-values.yaml|prometheus-stack/k8s/active"
"loki|grafana/loki|prometheus|7.3.0|loki/k8s/loki-values.yaml|loki/k8s/active"
"alloy|grafana/alloy|prometheus|1.12.1|loki/k8s/alloy-values.yaml|loki/k8s/active"
"reloader|stakater/reloader|reloader|2.2.17|reloader/k8s/reloader-values.yaml|reloader/k8s/active"
@@ -547,6 +558,7 @@ helm_repo_for() {
prometheus-community/*) echo "prometheus-community https://prometheus-community.github.io/helm-charts" ;;
grafana/*) echo "grafana https://grafana.github.io/helm-charts" ;;
stakater/*) echo "stakater https://stakater.github.io/stakater-charts" ;;
victoriametrics/*) echo "victoriametrics https://victoriametrics.github.io/helm-charts" ;;
esac
}
@@ -686,30 +698,74 @@ stage_preflight() {
git -C "$REPO" reset --hard "$target"
}
# Required pod Secrets, scoped to the resource namespace. TLS route Secrets are
# created by cert-manager and are not prerequisites for applying a Certificate.
check_referenced_secrets() {
local m k objects refs extracted ns name
local missing=()
refs=""
for m in ${K8S_MANIFESTS[@]+"${K8S_MANIFESTS[@]}"}; do
if skip_uninstalled_vmagent_crd "$m"; then
continue
fi
objects="$(kubectl create --dry-run=client --validate=false -f "$m" -o json)" || return 1
extracted="$(printf '%s' "$objects" | jq -r -f "$REPO/.gitea/workflows/secret-references.jq")" || return 1
refs+="$extracted"$'\n'
done
for k in ${KUSTOMIZE_APPS[@]+"${KUSTOMIZE_APPS[@]}"}; do
objects="$(kubectl kustomize "$k" | kubectl create --dry-run=client --validate=false -f - -o json)" || return 1
extracted="$(printf '%s' "$objects" | jq -r -f "$REPO/.gitea/workflows/secret-references.jq")" || return 1
refs+="$extracted"$'\n'
done
while read -r ns name; do
[ -n "${name:-}" ] || continue
if kubectl get secret "$name" -n "$ns" -o name >/dev/null 2>&1; then
echo " ok: $ns/$name"
else
echo " MISSING OR UNREADABLE: $ns/$name"
missing+=("$ns/$name")
fi
done < <(printf '%s' "$refs" | sort -u)
if [ "${#missing[@]}" -gt 0 ]; then
echo "ERROR: required pod Secrets are missing or unreadable:"
printf ' - %s\n' "${missing[@]}"
echo "Create them in the listed namespaces from the service's secret example."
return 1
fi
}
# The VMAgent CRD is installed by the VictoriaMetrics Operator Helm release in
# stage_apply_k8s, after this preflight stage. Skip only its dry-run until then.
skip_uninstalled_vmagent_crd() {
local manifest="$1"
if [[ "$manifest" == "$REPO/prometheus-stack/k8s/vmagent.yaml" ]] \
&& ! kubectl get crd vmagents.operator.victoriametrics.com >/dev/null 2>&1; then
echo " skip: VMAgent CRD is installed by Helm during apply: ${manifest#"$REPO"/}"
return 0
fi
return 1
}
stage_validate() {
check_prune_mode || return 1
cd "$REPO"
select_manifests
local m k cf
# Compose .env files and secret files are gitignored by design, so the
# workstation never has real values for the inactive stacks. This stage only
# runs the full check on active stacks; the general structure check for every
# committed Compose file (active or not) lives in the ci workflow, which has no
# .env at all.
#
# Active stacks are still validated with interpolation and env-file resolution
# off, so required-variable guards (:?) and missing local files do not fail the
# deploy. Normalization and consistency checks stay enabled.
# The deploy host has the local .env and secret files. Resolve them here so
# missing configuration fails before either apply job changes workloads.
# CI keeps the structure-only check for inactive stacks.
# shellcheck source=compose-lint.sh
source "$REPO/.gitea/workflows/compose-lint.sh"
local compose_validate_flags=()
mapfile -t compose_validate_flags < <(compose_safe_flags)
log "Validate compose stacks"
for cf in ${COMPOSE_STACKS[@]+"${COMPOSE_STACKS[@]}"}; do
echo " config: $cf"
validate_compose_file "$cf" ${compose_validate_flags[@]+"${compose_validate_flags[@]}"}
validate_compose_file "$cf"
done
log "Validate k8s manifests (kubectl dry-run=client)"
for m in ${K8S_MANIFESTS[@]+"${K8S_MANIFESTS[@]}"}; do
if skip_uninstalled_vmagent_crd "$m"; then
continue
fi
kubectl apply --dry-run=client -f "$m" >/dev/null
done
for k in ${KUSTOMIZE_APPS[@]+"${KUSTOMIZE_APPS[@]}"}; do
@@ -717,6 +773,9 @@ stage_validate() {
done
log "Validate k8s manifests (kubectl dry-run=server)"
for m in ${K8S_MANIFESTS[@]+"${K8S_MANIFESTS[@]}"}; do
if skip_uninstalled_vmagent_crd "$m"; then
continue
fi
kubectl apply --dry-run=server -f "$m" >/dev/null
done
for k in ${KUSTOMIZE_APPS[@]+"${KUSTOMIZE_APPS[@]}"}; do
@@ -724,48 +783,20 @@ stage_validate() {
done
log "Checking referenced Secrets exist"
echo " (deploy never applies *secret*.yaml; create missing ones manually)"
local ref_secrets=() missing_secrets=() all_secrets s
if [ "${#K8S_MANIFESTS[@]}" -gt 0 ]; then
while IFS= read -r s; do
[ -n "$s" ] && ref_secrets+=("$s")
done < <(
{
grep -h -A1 -E 'secretRef:|secretKeyRef:' "${K8S_MANIFESTS[@]}" 2>/dev/null || true
grep -h -E 'secretName:' "${K8S_MANIFESTS[@]}" 2>/dev/null || true
} | grep -E 'name:' | sed -E 's/.*name:[[:space:]]*//' | tr -d '"'"'"' "'"'" | sed -E 's/[[:space:]]*#.*//' | awk 'NF' | sort -u || true
)
fi
all_secrets="$(kubectl get secrets -A --no-headers -o custom-columns=:metadata.name 2>/dev/null || true)"
for s in ${ref_secrets[@]+"${ref_secrets[@]}"}; do
if printf '%s\n' "$all_secrets" | grep -qx "$s"; then
echo " ok: $s"
else
echo " MISSING: $s"
missing_secrets+=("$s")
fi
done
if [ "${#missing_secrets[@]}" -gt 0 ]; then
echo "ERROR: ${#missing_secrets[@]} referenced Secret(s) not found in the cluster:"
printf ' - %s\n' "${missing_secrets[@]}"
echo "Create them manually from the laptop, e.g.:"
echo " kubectl apply -f SERVICE/k8s/secrets.yaml # see SERVICE/k8s/secrets.yaml.example"
exit 1
fi
check_referenced_secrets
}
stage_apply_k8s() {
check_prune_mode || return 1
cd "$REPO"
select_manifests >/dev/null
local ns_files=() other_files=() m k prune_opts=()
local ns_files=() other_files=() m k
for m in ${K8S_MANIFESTS[@]+"${K8S_MANIFESTS[@]}"}; do
case "$m" in
*/namespace.y?ml) ns_files+=("$m") ;;
*) other_files+=("$m") ;;
esac
done
if [ "$APPLY_PRUNE" = "true" ]; then
prune_opts=(--prune -l app.kubernetes.io/managed-by=homelab-deploy)
fi
# Record what is about to change, and publish it for the verify job, before
# the first apply. Both are fatal on failure: see snapshot_dir.
@@ -790,7 +821,7 @@ stage_apply_k8s() {
if [ "${#other_files[@]}" -gt 0 ]; then
log "Applying resources (${#other_files[@]} files, our images pinned to digests)"
for m in "${other_files[@]}"; do
if ! render_pinned <"$m" | kubectl apply "${prune_opts[@]}" -f -; then
if ! render_pinned <"$m" | kubectl apply -f -; then
echo "ERROR: apply failed for ${m#"$REPO"/}" >&2
exit 1
fi
+8 -3
View File
@@ -5,6 +5,7 @@ on:
# workflow_dispatch so a red lint/validate run can never reach the cluster.
workflow_run:
workflows: [ci]
branches: [main]
types: [completed]
workflow_dispatch:
@@ -137,9 +138,10 @@ jobs:
# lets it start after a failed dependency; the needs on apply-compose are a
# barrier, so verification begins only once both applies are done.
verify-k8s:
needs: [apply-k8s, apply-compose]
needs: [preflight, apply-k8s, apply-compose]
if: >-
always() &&
needs.preflight.result == 'success' &&
needs.apply-k8s.result != 'skipped' &&
needs.apply-compose.result != 'skipped'
runs-on: [self-hosted, linux, arch, homelab, prod]
@@ -182,8 +184,11 @@ jobs:
# suppressing them on a rollback would hide the one run where the answer
# matters most.
smoke:
needs: [verify-k8s]
if: always() && needs.verify-k8s.result != 'skipped'
needs: [preflight, verify-k8s]
if: >-
always() &&
needs.preflight.result == 'success' &&
needs.verify-k8s.result != 'skipped'
runs-on: [self-hosted, linux, arch, homelab, prod]
timeout-minutes: 10
steps:
+10
View File
@@ -120,6 +120,15 @@ install_shellcheck() {
rm -rf "$tmp"
}
install_jq() {
if at_version jq "${JQ_VERSION}"; then
return 0
fi
fetch "https://github.com/jqlang/jq/releases/download/jq-${JQ_VERSION}/jq-linux-${goarch}" \
"$BIN_DIR/jq"
chmod 0755 "$BIN_DIR/jq"
}
install_uv() {
if at_version uv "${UV_VERSION}"; then
return 0
@@ -236,6 +245,7 @@ for tool in "${wanted[@]}"; do
case "$tool" in
kubeconform) install_kubeconform ;;
shellcheck) install_shellcheck ;;
jq) install_jq ;;
actionlint) install_actionlint ;;
prettier) install_prettier ;;
ruff) install_ruff ;;
+14
View File
@@ -2,9 +2,23 @@ name: renovate-ci
on:
pull_request:
paths:
- "renovate/**"
- ".gitea/workflows/renovate-ci.yaml"
- ".gitea/workflows/sync-renovate-configmap.sh"
- ".gitea/workflows/compose-lint.sh"
- ".gitea/workflows/install-ci-tools.sh"
- ".gitea/workflows/tool-versions.env"
push:
branches:
- main
paths:
- "renovate/**"
- ".gitea/workflows/renovate-ci.yaml"
- ".gitea/workflows/sync-renovate-configmap.sh"
- ".gitea/workflows/compose-lint.sh"
- ".gitea/workflows/install-ci-tools.sh"
- ".gitea/workflows/tool-versions.env"
workflow_dispatch:
permissions:
+13
View File
@@ -0,0 +1,13 @@
# kubectl emits a List for files containing multiple resources.
(if .kind == "List" then .items[] else . end)
| (.metadata.namespace // "default") as $ns
| [
(.. | objects
| (.secretRef? // empty), (.secretKeyRef? // empty), (.secret? // empty)
| select(.optional != true)
| .name // .secretName // empty),
(.. | objects | .imagePullSecrets[]?.name)
]
| unique[]
| select(. != null and . != "")
| "\($ns) \(.)"
+3
View File
@@ -31,3 +31,6 @@ UV_VERSION="0.12.17"
# so the tree that gets tested is the tree that gets built. Renovate keeps this
# in step with the Dockerfile's node: tag via the "node runtime" group.
NODE_VERSION="22.23.3"
# Secret-reference regression tests parse rendered Kubernetes objects.
JQ_VERSION="1.8.1"
+1 -1
View File
@@ -31,7 +31,7 @@ services:
- "traefik.http.routers.adguard-dev.entrypoints=websecure"
- "traefik.http.routers.adguard-dev.tls=true"
# DoH Router
- "traefik.http.routers.dns-over-https.rule=(Host(`dns.forust.xyz` || Host(`adguard.forust.xyz`)) && PathPrefix(`/dns-query`))"
- "traefik.http.routers.dns-over-https.rule=(Host(`dns.forust.xyz`) || Host(`adguard.forust.xyz`)) && PathPrefix(`/dns-query`)"
- "traefik.http.routers.dns-over-https.entrypoints=websecure"
- "traefik.http.routers.dns-over-https.tls.certresolver=letsencrypt"
+2 -2
View File
@@ -51,6 +51,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: adguard-deployment
namespace: adguard
spec:
@@ -64,8 +66,6 @@ spec:
metadata:
labels:
app: adguard
annotations:
reloader.stakater.com/auto: "true"
spec:
containers:
- name: adguard
+4
View File
@@ -27,6 +27,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: authentik-server-deployment
namespace: authentik
spec:
@@ -63,6 +65,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: authentik-worker-deployment
namespace: authentik
spec:
+2
View File
@@ -1,6 +1,8 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: cfddns
labels:
app: cfddns
+2
View File
@@ -17,6 +17,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: checkmk-deployment
namespace: checkmk
spec:
+3 -1
View File
@@ -1,6 +1,8 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: cloudflared
labels:
app: cloudflared
@@ -18,7 +20,7 @@ spec:
spec:
containers:
- name: cloudflared
image: cloudflare/cloudflared:2026.9.3
image: cloudflare/cloudflared:2026.10.0
imagePullPolicy: IfNotPresent
args:
- tunnel
+2
View File
@@ -13,6 +13,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: convertx-deployment
namespace: converters
spec:
+14
View File
@@ -0,0 +1,14 @@
# EDU deployment ownership
Application source and release builds: `forust/edu-master`.
The homelab pipeline deploys `edu_master/k8s` and preserves explicit image digests.
The application copies in this directory are legacy and are not build inputs.
Do not publish EDU `prod` images from homelab or resolve releases from moving tags.
For an EDU release, validate both images, select their digests in the keeper and
checker manifests, and run the existing homelab validation/apply/verification
helpers against this service. Keep the existing Secret and Redis PVC.
Coordinate Redis authentication changes with both clients and all init/probes;
keep a pre-rollout Redis backup and both previous compatible image references.
The current HTTP checker does not depend on Playwright; check other consumers
before removing the separate browser service.
+31 -12
View File
@@ -50,7 +50,36 @@ spec:
severity: critical
annotations:
summary: "Webinar checker failing consecutively"
description: 'edu-master/webinar-checker: {{ $value }} consecutive webinar check failures (timeout / playwright error / page error). Check pod logs (Loki: {namespace="edu-master", container="webinar-checker"}).'
description: 'edu-master/webinar-checker: {{ $value }} consecutive webinar check failures (timeout / http error / page error). Check pod logs (Loki: {namespace="edu-master", container="webinar-checker"}).'
- alert: WebinarCheckerNeverStarted
expr: |
(time() - edu_process_start > 120)
and (webinar_check_last_run_timestamp_seconds == 0)
for: 2m
labels:
severity: critical
annotations:
summary: "Webinar checker job has not started"
description: "The process exposes metrics but its webinar job has never started."
- alert: WebinarDeliveryPending
expr: edu_delivery_pending > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Webinar notifications await delivery"
description: "Telegram delivery has pending recipients. Check delivery failures and retry status."
- alert: EduRedisUnavailable
expr: edu_redis_connected == 0
for: 2m
labels:
severity: critical
annotations:
summary: "EDU checker cannot reach Redis"
description: "Redis health checks are failing; checker commands and delivery may be unavailable."
# Metrics endpoint not scraped for 10m: pod down, metrics server dead, or ServiceMonitor broken.
- alert: WebinarCheckerScrapeDown
@@ -74,7 +103,7 @@ spec:
summary: "EDU_PHPSESSID missing"
description: "edu-master: EDU_PHPSESSID absent from redis for 10m. Webinar/diari/schedule checks are all skipped. Check session-keeper logs and EDU credentials."
# Hard deps: checker and playwright deployments unavailable.
# Hard deps: checker deployment unavailable.
- alert: WebinarCheckerDeploymentDown
expr: |
kube_deployment_status_replicas_unavailable{deployment="webinar-checker", namespace="edu-master"} > 0
@@ -84,13 +113,3 @@ spec:
annotations:
summary: "Webinar checker deployment unavailable"
description: "edu-master/webinar-checker deployment has {{ $value }} unavailable replica(s) for 10m."
- alert: PlaywrightServiceDown
expr: |
kube_deployment_status_replicas_unavailable{deployment="playwright-service", namespace="edu-master"} > 0
for: 10m
labels:
severity: critical
annotations:
summary: "Playwright service unavailable"
description: "edu-master/playwright-service deployment has {{ $value }} unavailable replica(s) for 10m. All webinar/diari/schedule checks fail without it."
+22
View File
@@ -0,0 +1,22 @@
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: redis-clients-only
namespace: edu-master
spec:
podSelector:
matchLabels:
app: edu-master-redis
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: edu-master-session-keeper
- podSelector:
matchLabels:
app: edu-master-webinar-checker
ports:
- protocol: TCP
port: 6379
+21
View File
@@ -20,6 +20,27 @@ spec:
- name: redis
image: redis:8.10.2-alpine
imagePullPolicy: IfNotPresent
env:
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: edu-master-secrets
key: REDIS_PASSWORD
- name: REDISCLI_AUTH
valueFrom:
secretKeyRef:
name: edu-master-secrets
key: REDIS_PASSWORD
command:
- /bin/sh
- -ec
- |
case "$REDIS_PASSWORD" in *[!0-9a-fA-F]*|'') echo 'REDIS_PASSWORD must be 64 hex characters' >&2; exit 1;; esac
[ "${#REDIS_PASSWORD}" -eq 64 ] || { echo 'REDIS_PASSWORD must be 64 hex characters' >&2; exit 1; }
umask 077
printf 'requirepass "%s"\n' "$REDIS_PASSWORD" > /tmp/redis-auth.conf
chown redis:redis /tmp/redis-auth.conf
exec docker-entrypoint.sh redis-server /tmp/redis-auth.conf
ports:
- containerPort: 6379
volumeMounts:
+17 -1
View File
@@ -1,6 +1,8 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: session-keeper
namespace: edu-master
labels:
@@ -14,12 +16,20 @@ spec:
type: Recreate
template:
metadata:
annotations:
edu.forust.xyz/source-commit: "90829d6c8080b9928f9da23587678e640939e10a"
labels:
app: edu-master-session-keeper
spec:
initContainers:
- name: wait-redis
image: redis:8.10.2-alpine
env:
- name: REDISCLI_AUTH
valueFrom:
secretKeyRef:
name: edu-master-secrets
key: REDIS_PASSWORD
command:
- /bin/sh
- -ec
@@ -33,10 +43,16 @@ spec:
echo "redis is ready"
containers:
- name: session-keeper
image: gcr.forust.xyz/forust/session-keeper:prod
image: gcr.forust.xyz/forust/session-keeper@sha256:49285e87cc5bc4cf4ffe190813d87927916c2df8a206daac0aeb7d227c636450
envFrom:
- secretRef:
name: edu-master-secrets
env:
- name: REDISCLI_AUTH
valueFrom:
secretKeyRef:
name: edu-master-secrets
key: REDIS_PASSWORD
resources:
requests:
cpu: 25m
+20 -8
View File
@@ -1,6 +1,8 @@
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: webinar-checker
namespace: edu-master
labels:
@@ -14,14 +16,22 @@ spec:
type: Recreate
template:
metadata:
annotations:
edu.forust.xyz/source-commit: "90829d6c8080b9928f9da23587678e640939e10a"
labels:
app: edu-master-webinar-checker
spec:
# Enforces dependency order like compose depends_on:
# redis healthy -> session-keeper healthy (EXISTS EDU_PHPSESSID) -> playwright started
# redis healthy -> session-keeper healthy (EXISTS EDU_PHPSESSID)
initContainers:
- name: wait-deps
image: redis:8.10.2-alpine
env:
- name: REDISCLI_AUTH
valueFrom:
secretKeyRef:
name: edu-master-secrets
key: REDIS_PASSWORD
command:
- /bin/sh
- -ec
@@ -39,15 +49,9 @@ spec:
sleep 2
done
echo "PHPSESSID ok"
until nc -z playwright-service 3000; do
i=$((i+1))
[ "$i" -ge 300 ] && echo "TIMEOUT: playwright-service not reachable" && exit 1
sleep 2
done
echo "playwright ok"
containers:
- name: webinar-checker
image: gcr.forust.xyz/forust/webinar-checker:prod
image: gcr.forust.xyz/forust/webinar-checker@sha256:66c146f7b43cb9f0dc31ba9aa36d217e01df42ddafba5971b79c12ec215b2c01
ports:
- name: metrics
containerPort: 8000
@@ -60,6 +64,14 @@ spec:
timeoutSeconds: 3
failureThreshold: 12
initialDelaySeconds: 10
livenessProbe:
httpGet:
path: /live
port: metrics
initialDelaySeconds: 60
periodSeconds: 15
timeoutSeconds: 3
failureThreshold: 4
envFrom:
- secretRef:
name: edu-master-secrets
+1 -1
View File
@@ -1,4 +1,4 @@
FROM python:3.11-slim
FROM python:3.14-slim
WORKDIR /app
+1 -1
View File
@@ -1,4 +1,4 @@
FROM python:3.11-slim
FROM python:3.14-slim
WORKDIR /app
+2
View File
@@ -17,6 +17,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: gitea-deployment
namespace: gitea
spec:
+3 -1
View File
@@ -13,6 +13,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: glance-deployment
namespace: glance
spec:
@@ -69,7 +71,7 @@ spec:
name: glance-config
- name: glance-assets
configMap:
name: glance-config
name: glance-assets
- name: docker-socket
hostPath:
path: /var/run/docker.sock
+1 -1
View File
@@ -1,7 +1,7 @@
services:
homarr:
container_name: homarr
image: ghcr.io/homarr-labs/homarr:v2.1.2
image: ghcr.io/homarr-labs/homarr:v2.2.0
restart: unless-stopped
volumes:
- ./appdata:/appdata
+3 -1
View File
@@ -13,6 +13,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: homarr-deployment
namespace: homarr
spec:
@@ -30,7 +32,7 @@ spec:
serviceAccountName: homarr
containers:
- name: homarr
image: ghcr.io/homarr-labs/homarr:v2.1.2
image: ghcr.io/homarr-labs/homarr:v2.2.0
envFrom:
- configMapRef:
name: homarr-config
+2
View File
@@ -14,6 +14,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: immich-deployment
namespace: immich
labels:
+2
View File
@@ -14,6 +14,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: immich-machine-learning-deployment
namespace: immich
labels:
+2
View File
@@ -17,6 +17,8 @@ spec:
apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: immich-valkey
namespace: immich
labels:
+2
View File
@@ -13,6 +13,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: kener-deployment
namespace: kener
spec:
+2
View File
@@ -13,6 +13,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: metube-deployment
namespace: metube
spec:
+1 -1
View File
@@ -1,6 +1,6 @@
services:
n8n:
image: docker.n8n.io/n8nio/n8n:2.42.3
image: docker.n8n.io/n8nio/n8n:2.43.0
container_name: n8n
restart: unless-stopped
environment:
+3 -1
View File
@@ -13,6 +13,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: n8n-deployment
namespace: n8n
spec:
@@ -29,7 +31,7 @@ spec:
spec:
containers:
- name: n8n
image: docker.n8n.io/n8nio/n8n:2.42.3
image: docker.n8n.io/n8nio/n8n:2.43.0
envFrom:
- configMapRef:
name: n8n-config
+109
View File
@@ -0,0 +1,109 @@
#!/bin/sh
set -eu
umask 077
TEMPLATE_PATH=/opt/netbird/config.template.yaml
RENDERED_PATH=/run/netbird/config.yaml
RELAY_SECRET_PATH=/run/secrets/relay_auth_secret
ENCRYPTION_KEY_PATH=/run/secrets/datastore_encryption_key
is_valid_proxy_subnet() {
candidate="$1"
case "$candidate" in
0.0.0.0/0)
return 1
;;
*/*)
address="${candidate%%/*}"
prefix="${candidate#*/}"
;;
*)
return 1
;;
esac
case "$prefix" in
0|[1-9]|[1-2][0-9]|3[0-2]) ;;
*)
return 1
;;
esac
old_ifs="$IFS"
IFS=.
# shellcheck disable=SC2086
set -- $address
IFS="$old_ifs"
[ "$#" -eq 4 ] || return 1
for octet do
case "$octet" in
0|[1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]) ;;
*)
return 1
;;
esac
done
}
read_secret() {
secret_path="$1"
if [ ! -r "$secret_path" ]; then
echo "Required secret is not readable: $secret_path" >&2
exit 1
fi
secret_value="$(cat "$secret_path")"
if [ -z "$secret_value" ]; then
echo "Required secret is empty: $secret_path" >&2
exit 1
fi
printf '%s' "$secret_value"
}
if [ -z "${NETBIRD_DOMAIN:-}" ]; then
echo "NETBIRD_DOMAIN must be set" >&2
exit 1
fi
case "$NETBIRD_DOMAIN" in
*[!A-Za-z0-9.-]*)
echo "NETBIRD_DOMAIN contains unsupported characters" >&2
exit 1
;;
esac
if [ -z "${NETBIRD_PROXY_SUBNET:-}" ] || [ "$NETBIRD_PROXY_SUBNET" = "auto" ]; then
echo "NETBIRD_PROXY_SUBNET must be an explicit IPv4 CIDR; run netbird/setup.sh first" >&2
exit 1
fi
if ! is_valid_proxy_subnet "$NETBIRD_PROXY_SUBNET"; then
echo "NETBIRD_PROXY_SUBNET must be a non-default IPv4 CIDR, for example 172.20.0.0/16" >&2
exit 1
fi
if [ "$#" -ne 2 ] || [ "$1" != "--config" ] || [ "$2" != "$RENDERED_PATH" ]; then
echo "Expected: --config $RENDERED_PATH" >&2
exit 1
fi
relay_secret="$(read_secret "$RELAY_SECRET_PATH")"
encryption_key="$(read_secret "$ENCRYPTION_KEY_PATH")"
mkdir -p "$(dirname "$RENDERED_PATH")"
sed \
-e "s|__NETBIRD_DOMAIN__|${NETBIRD_DOMAIN}|g" \
-e "s|__NETBIRD_AUTH_SECRET__|${relay_secret}|g" \
-e "s|__NETBIRD_ENCRYPTION_KEY__|${encryption_key}|g" \
-e "s|__NETBIRD_PROXY_SUBNET__|${NETBIRD_PROXY_SUBNET}|g" \
"$TEMPLATE_PATH" >"$RENDERED_PATH"
if grep -q '__NETBIRD_' "$RENDERED_PATH"; then
echo "Rendered NetBird configuration still contains unresolved placeholders" >&2
exit 1
fi
exec /go/bin/netbird-server "$@"
+4
View File
@@ -32,6 +32,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: netbird-server-deployment
namespace: netbird
spec:
@@ -126,6 +128,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: netbird-dashboard-deployment
namespace: netbird
spec:
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Prepare local Compose configuration without replacing existing credentials.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"
umask 077
if [ ! -f .env ]; then
cp .env.example .env
fi
if grep -q '^NETBIRD_PROXY_SUBNET=auto$' .env; then
subnet="$(docker network inspect proxy --format '{{range .IPAM.Config}}{{println .Subnet}}{{end}}' | awk '/^[0-9]+\./ { print; exit }')"
if [ -z "$subnet" ]; then
echo "No IPv4 subnet found on the Docker proxy network. Set NETBIRD_PROXY_SUBNET in .env." >&2
exit 1
fi
# The detected value must be safe to substitute into the env file.
if [[ ! "$subnet" =~ ^[0-9.]+/[0-9]+$ ]]; then
echo "Unexpected Docker network subnet: $subnet" >&2
exit 1
fi
sed -i "s|^NETBIRD_PROXY_SUBNET=auto$|NETBIRD_PROXY_SUBNET=$subnet|" .env
fi
mkdir -p secrets
chmod 700 secrets
for name in relay-auth-secret datastore-encryption-key; do
path="secrets/$name"
if [ -e "$path" ]; then
if [ ! -s "$path" ]; then
echo "Existing secret is empty: $path. Restore it before continuing." >&2
exit 1
fi
else
openssl rand -base64 32 >"$path"
fi
chmod 600 "$path"
done
printf '%s\n' 'Local files are ready. Review .env, then run docker compose config --quiet.'
+4
View File
@@ -14,6 +14,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: netbox-deployment
namespace: netbox
labels:
@@ -118,6 +120,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: netbox-worker-deployment
namespace: netbox
labels:
+2
View File
@@ -17,6 +17,8 @@ spec:
apiVersion: apps/v1
kind: StatefulSet
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: netbox-valkey
namespace: netbox
labels:
+1 -1
View File
@@ -1,6 +1,6 @@
services:
netronome:
image: ghcr.io/autobrr/netronome:v0.15.0
image: ghcr.io/autobrr/netronome:v0.16.0
restart: unless-stopped
container_name: netronome
ports:
+3 -1
View File
@@ -14,6 +14,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: netronome-deployment
namespace: netronome
labels:
@@ -32,7 +34,7 @@ spec:
spec:
containers:
- name: netronome
image: ghcr.io/autobrr/netronome:v0.15.0
image: ghcr.io/autobrr/netronome:v0.16.0
ports:
- name: netronome-port
protocol: TCP
+1
View File
@@ -1,6 +1,7 @@
POSTGRES_ADMIN_PASSWORD=
AUTHENTIK_DB_PASSWORD=
GITEA_DB_PASSWORD=
NETBOX_DB_PASSWORD=
NETRONOME_DB_PASSWORD=
PENPOT_DB_PASSWORD=
STATUSPAGE_DB_PASSWORD=
+17
View File
@@ -0,0 +1,17 @@
# VictoriaMetrics
The `victoria-operator` Helm release converts Prometheus Operator
`ServiceMonitor` resources into owned `VMServiceScrape` resources. The
`VMAgent` selects converted scrapes labeled `release: prometheus-stack` in all
namespaces and writes them to the existing single-node VictoriaMetrics
instance. Changes to selected `ServiceMonitor` resources are reconciled
automatically; there is no copied Prometheus scrape-config blob to regenerate.
The agent drops targets for the Prometheus server service to avoid duplicating
its self-scrape. `scraper: victoria` identifies the samples ingested by this
VMAgent.
The VictoriaMetrics Operator chart and its CRDs are installed before the
Kubernetes manifests by the normal deploy workflow. On a cluster where the
operator CRDs are not installed yet, CI skips the server-side dry-run of the
`VMAgent` resource; the deploy installs the chart before applying that resource.
+11
View File
@@ -38,6 +38,8 @@ grafana:
# One block covers both the dashboards and datasources sidecars (p95 91M / 80M).
sidecar:
datasources:
defaultDatasourceEnabled: false
resources:
requests:
memory: "96Mi"
@@ -50,9 +52,18 @@ grafana:
type: loki
url: http://loki-gateway.prometheus.svc.cluster.local
access: proxy
- name: VictoriaMetrics
type: prometheus
url: http://victoria-metrics.prometheus.svc.cluster.local:8428
access: proxy
isDefault: true
prometheus:
prometheusSpec:
# VM trial: vmagent scrapes and remote-writes to VictoriaMetrics, so the
# Prometheus server itself stands down. Encoded here (not a kubectl patch)
# so helm keeps owning spec.replicas and upgrades do not conflict on it.
replicas: 0
retention: 60d
retentionSize: 32GB
storageSpec:
+102
View File
@@ -31,3 +31,105 @@ spec:
port: 80
tls:
secretName: internal-wildcard-tls
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: prometheus-local
namespace: prometheus
spec:
entryPoints:
- websecure
routes:
- match: Host(`prom.workstation.internal`) || Host(`prom.gigaforust.internal`)
kind: Rule
services:
- name: prometheus-stack-kube-prom-prometheus
port: 9090
tls:
secretName: internal-wildcard-tls
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: alertmanager-local
namespace: prometheus
spec:
entryPoints:
- websecure
routes:
- match: Host(`am.workstation.internal`) || Host(`am.gigaforust.internal`)
kind: Rule
services:
- name: prometheus-stack-kube-prom-alertmanager
port: 9093
tls:
secretName: internal-wildcard-tls
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: loki-local
namespace: prometheus
spec:
entryPoints:
- websecure
routes:
- match: Host(`loki.workstation.internal`) || Host(`loki.gigaforust.internal`)
kind: Rule
services:
- name: loki-gateway
port: 80
tls:
secretName: internal-wildcard-tls
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: alloy-local
namespace: prometheus
spec:
entryPoints:
- websecure
routes:
- match: Host(`alloy.workstation.internal`) || Host(`alloy.gigaforust.internal`)
kind: Rule
services:
- name: alloy
port: 12345
tls:
secretName: internal-wildcard-tls
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: victoria-local
namespace: prometheus
spec:
entryPoints:
- websecure
routes:
- match: Host(`victoria.workstation.internal`) || Host(`victoria.gigaforust.internal`)
kind: Rule
services:
- name: victoria-metrics
port: 8428
tls:
secretName: internal-wildcard-tls
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: vmalert-local
namespace: prometheus
spec:
entryPoints:
- websecure
routes:
- match: Host(`vmalert.workstation.internal`) || Host(`vmalert.gigaforust.internal`)
kind: Rule
services:
- name: vmalert
port: 8880
tls:
secretName: internal-wildcard-tls
@@ -0,0 +1,12 @@
nameOverride: victoria-operator
operator:
enable_converter_ownership: true
resources:
requests:
cpu: 50m
memory: 96Mi
limits:
cpu: 200m
memory: 256Mi
+79
View File
@@ -0,0 +1,79 @@
apiVersion: v1
kind: Service
metadata:
name: victoria-metrics
namespace: prometheus
spec:
selector:
app: victoria-metrics
ports:
- port: 8428
targetPort: 8428
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: victoria-pvc
namespace: prometheus
spec:
resources:
requests:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: victoria-deployment
namespace: prometheus
spec:
replicas: 1
selector:
matchLabels:
app: victoria-metrics
strategy:
type: Recreate
template:
metadata:
labels:
app: victoria-metrics
spec:
containers:
- name: victoria
image: victoriametrics/victoria-metrics:v1.153.0-scratch
args:
- -storageDataPath=/vmdata
- -retentionPeriod=30d
- -httpListenAddr=:8428
ports:
- containerPort: 8428
readinessProbe:
httpGet:
path: /health
port: 8428
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 6
livenessProbe:
httpGet:
path: /health
port: 8428
initialDelaySeconds: 60
periodSeconds: 30
failureThreshold: 3
volumeMounts:
- name: vmdata
mountPath: /vmdata
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "1000m"
memory: "1Gi"
volumes:
- name: vmdata
persistentVolumeClaim:
claimName: victoria-pvc
+29
View File
@@ -0,0 +1,29 @@
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMAgent
metadata:
name: vmagent
namespace: prometheus
spec:
image:
tag: v1.153.0
scrapeInterval: 30s
externalLabels:
scraper: victoria
serviceScrapeNamespaceSelector: {}
serviceScrapeSelector:
matchLabels:
release: prometheus-stack
globalScrapeRelabelConfigs:
- action: drop
source_labels:
- __meta_kubernetes_service_name
regex: prometheus-stack-kube-prom-prometheus
remoteWrite:
- url: http://victoria-metrics.prometheus.svc.cluster.local:8428/api/v1/write
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1000m"
memory: 1Gi
+70
View File
@@ -0,0 +1,70 @@
apiVersion: v1
kind: Service
metadata:
name: vmalert
namespace: prometheus
spec:
selector:
app: vmalert
ports:
- port: 8880
targetPort: 8880
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: vmalert-deployment
namespace: prometheus
spec:
replicas: 1
selector:
matchLabels:
app: vmalert
strategy:
type: Recreate
template:
metadata:
labels:
app: vmalert
spec:
containers:
- name: vmalert
image: victoriametrics/vmalert:v1.153.0
args:
- -datasource.url=http://victoria-metrics.prometheus.svc.cluster.local:8428
- -remoteWrite.url=http://victoria-metrics.prometheus.svc.cluster.local:8428
- -notifier.url=http://prometheus-stack-kube-prom-alertmanager.prometheus.svc.cluster.local:9093
- -rule=/etc/vm/rules/*.yaml
- -evaluationInterval=60s
- -httpListenAddr=:8880
ports:
- containerPort: 8880
readinessProbe:
httpGet:
path: /metrics
port: 8880
initialDelaySeconds: 15
periodSeconds: 10
failureThreshold: 6
livenessProbe:
httpGet:
path: /metrics
port: 8880
initialDelaySeconds: 60
periodSeconds: 30
failureThreshold: 3
volumeMounts:
- name: rules
mountPath: /etc/vm/rules
readOnly: true
resources:
requests:
cpu: "50m"
memory: "64Mi"
limits:
cpu: "200m"
memory: "256Mi"
volumes:
- name: rules
configMap:
name: prometheus-prometheus-stack-kube-prom-prometheus-rulefiles-0
View File
Whitespace-only changes.
+7 -1
View File
@@ -1,11 +1,17 @@
# Pinned chart: stakater/reloader 2.2.17 (app v1.4.22).
# Deployed by the deploy workflow, namespace reloader.
# Restarts pods when a ConfigMap or Secret they consume changes. Opt-in per workload
# via the reloader.stakater.com/auto: "true" pod annotation; watchGlobally because
# via the reloader.stakater.com/auto: "true" workload annotation; watchGlobally because
# the workloads that need it are spread across a few dozen namespaces.
reloader:
watchGlobally: true
# Only opted-in workloads are restarted. Keep scheduled jobs on their schedule.
autoReloadAll: false
ignoreJobs: true
ignoreCronJobs: true
# Change pod-template annotations rather than injecting STAKATER_* env vars.
reloadStrategy: annotations
deployment:
replicas: 1
+48 -25
View File
@@ -19,6 +19,9 @@ data:
"dependencyDashboard": true,
"prCreation": "immediate",
"labels": ["dependencies", "automated"],
"docker-compose": {
"managerFilePatterns": ["renovate/renovate-compose.yaml"]
},
"helm-values": {
"managerFilePatterns": ["/k8s/.+values\\.ya?ml$/"]
},
@@ -29,7 +32,7 @@ data:
{
"customType": "regex",
"description": "singlesource: playwright npm version pinned in npx command (k8s + compose)",
"managerFilePatterns": ["^edu_master/k8s/playwright\\.yaml$", "^edu_master/compose\\.yaml$"],
"managerFilePatterns": ["edu_master/k8s/playwright.yaml", "edu_master/compose.yaml"],
"matchStrings": ["playwright@(?<currentValue>\\d+\\.\\d+\\.\\d+)"],
"datasourceTemplate": "npm",
"depNameTemplate": "playwright"
@@ -37,24 +40,42 @@ data:
{
"customType": "regex",
"description": "singlesource: PLAYWRIGHT_VERSION file",
"managerFilePatterns": ["^edu_master/PLAYWRIGHT_VERSION$"],
"matchStrings": ["^(?<currentValue>\\d+\\.\\d+\\.\\d+)$"],
"managerFilePatterns": ["edu_master/PLAYWRIGHT_VERSION"],
"matchStrings": ["^(?<currentValue>\\d+\\.\\d+\\.\\d+)(?:\\r?\\n)?$"],
"datasourceTemplate": "pypi",
"depNameTemplate": "playwright"
},
{
"customType": "regex",
"description": "singlesource: playwright Python client version pinned in Dockerfile ARG",
"managerFilePatterns": ["edu_master/webinar-checker/Dockerfile"],
"matchStrings": ["(?:^|\\n)ARG PLAYWRIGHT_VERSION=(?<currentValue>\\d+\\.\\d+\\.\\d+)(?:\\r?\\n|$)"],
"datasourceTemplate": "pypi",
"depNameTemplate": "playwright",
"versioningTemplate": "pep440"
},
{
"customType": "regex",
"description": "kube-prometheus-stack chart version pinned in the deploy workflow",
"managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"],
"managerFilePatterns": [".gitea/workflows/deploy-lib.sh"],
"matchStrings": ["\\|prometheus-community/kube-prometheus-stack\\|prometheus\\|(?<currentValue>[0-9.]+)\\|"],
"datasourceTemplate": "helm",
"depNameTemplate": "kube-prometheus-stack",
"registryUrlTemplate": "https://prometheus-community.github.io/helm-charts"
},
{
"customType": "regex",
"description": "VictoriaMetrics Operator chart version pinned in the deploy workflow",
"managerFilePatterns": [".gitea/workflows/deploy-lib.sh"],
"matchStrings": ["\\|victoriametrics/victoria-metrics-operator\\|prometheus\\|(?<currentValue>[0-9.]+)\\|"],
"datasourceTemplate": "helm",
"depNameTemplate": "victoria-metrics-operator",
"registryUrlTemplate": "https://victoriametrics.github.io/helm-charts"
},
{
"customType": "regex",
"description": "grafana/loki chart version pinned in the deploy workflow",
"managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"],
"managerFilePatterns": [".gitea/workflows/deploy-lib.sh"],
"matchStrings": ["\\|grafana/loki\\|prometheus\\|(?<currentValue>[0-9.]+)\\|"],
"datasourceTemplate": "helm",
"depNameTemplate": "loki",
@@ -63,7 +84,7 @@ data:
{
"customType": "regex",
"description": "grafana/alloy chart version pinned in the deploy workflow",
"managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"],
"managerFilePatterns": [".gitea/workflows/deploy-lib.sh"],
"matchStrings": ["\\|grafana/alloy\\|prometheus\\|(?<currentValue>[0-9.]+)\\|"],
"datasourceTemplate": "helm",
"depNameTemplate": "alloy",
@@ -72,7 +93,7 @@ data:
{
"customType": "regex",
"description": "actionlint version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)ACTIONLINT_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "github-tags",
"depNameTemplate": "rhysd/actionlint"
@@ -80,7 +101,7 @@ data:
{
"customType": "regex",
"description": "shellcheck version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)SHELLCHECK_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "github-tags",
"depNameTemplate": "koalaman/shellcheck"
@@ -88,7 +109,7 @@ data:
{
"customType": "regex",
"description": "kubeconform version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)KUBECONFORM_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "github-tags",
"depNameTemplate": "yannh/kubeconform"
@@ -96,7 +117,7 @@ data:
{
"customType": "regex",
"description": "uv version used to build the pytest venv",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)UV_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "github-tags",
"depNameTemplate": "astral-sh/uv"
@@ -104,7 +125,7 @@ data:
{
"customType": "regex",
"description": "prettier version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)PRETTIER_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "npm",
"depNameTemplate": "prettier"
@@ -112,7 +133,7 @@ data:
{
"customType": "regex",
"description": "ruff version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)RUFF_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "pypi",
"depNameTemplate": "ruff"
@@ -120,7 +141,7 @@ data:
{
"customType": "regex",
"description": "pip-audit version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)PIP_AUDIT_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "pypi",
"depNameTemplate": "pip-audit"
@@ -128,7 +149,7 @@ data:
{
"customType": "regex",
"description": "yamllint version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)YAMLLINT_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "pypi",
"depNameTemplate": "yamllint"
@@ -136,7 +157,7 @@ data:
{
"customType": "regex",
"description": "hadolint version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)HADOLINT_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "github-tags",
"depNameTemplate": "hadolint/hadolint"
@@ -144,7 +165,7 @@ data:
{
"customType": "regex",
"description": "node version the ci workflow runs npm with",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)NODE_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "node",
"depNameTemplate": "node"
@@ -152,7 +173,7 @@ data:
{
"customType": "regex",
"description": "stakater/reloader chart version pinned in the deploy workflow",
"managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"],
"managerFilePatterns": [".gitea/workflows/deploy-lib.sh"],
"matchStrings": ["\\|stakater/reloader\\|reloader\\|(?<currentValue>[0-9.]+)\\|"],
"datasourceTemplate": "helm",
"depNameTemplate": "reloader",
@@ -161,7 +182,7 @@ data:
],
"packageRules": [
{
"description": "Automerge digest and patch updates - safe by definition, review adds nothing, keeps the renovate queue and the deploy line short. Specific no-automerge rules below still override this for playwright, helm and majors.",
"description": "Automerge ordinary digest and patch updates after successful checks; specific manual-review rules below override this.",
"matchUpdateTypes": ["digest", "patch"],
"automerge": true
},
@@ -172,6 +193,12 @@ data:
"groupSlug": "all-minor",
"automerge": false
},
{
"description": "Group ordinary patch updates; the specific groups and manual-review rules below take precedence",
"matchUpdateTypes": ["patch"],
"groupName": "all patch updates",
"groupSlug": "all-patch"
},
{
"description": "Keep private homelab images unchanged",
"matchDatasources": ["docker"],
@@ -196,7 +223,7 @@ data:
"automerge": false
},
{
"description": "CI runs npm on the node the panel image is built from - the NODE_VERSION pin in tool-versions.env and node:22-alpine in the Dockerfile are the same dependency and move as one",
"description": "Keep CI Node runtime updates in a separate, manually reviewed group",
"matchPackageNames": ["node"],
"groupName": "node runtime",
"groupSlug": "node",
@@ -205,6 +232,8 @@ data:
{
"description": "Helm chart bumps change PVC fields and admission behaviour, keep them reviewable",
"matchDatasources": ["helm"],
"groupName": "Helm chart {{depName}}",
"groupSlug": "helm-{{depName}}",
"automerge": false
},
{
@@ -213,12 +242,6 @@ data:
"dependencyDashboardApproval": true,
"automerge": false
},
{
"description": "Group patch updates from all sources - automerge still applies via the digest/patch rule above (helm/playwright stay manual via their own rules)",
"matchUpdateTypes": ["patch"],
"groupName": "all patch updates",
"groupSlug": "all-patch"
},
{
"description": "Python Y-bumps break compat (3.11->3.12->3.13->3.14) - keep the base image out of the shared minor/patch groups, review every bump separately. Placed last so its groupName wins.",
"matchDatasources": ["docker"],
+1 -1
View File
@@ -19,7 +19,7 @@ spec:
restartPolicy: Never
containers:
- name: renovate
image: renovate/renovate:44.136.0
image: renovate/renovate:44.140.0
env:
- name: RENOVATE_PLATFORM
value: gitea
+1 -1
View File
@@ -2,7 +2,7 @@ services:
renovate:
# 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
image: renovate/renovate:44.136.0
container_name: renovate
restart: "no"
env_file:
+48 -25
View File
@@ -8,6 +8,9 @@
"dependencyDashboard": true,
"prCreation": "immediate",
"labels": ["dependencies", "automated"],
"docker-compose": {
"managerFilePatterns": ["renovate/renovate-compose.yaml"]
},
"helm-values": {
"managerFilePatterns": ["/k8s/.+values\\.ya?ml$/"]
},
@@ -18,7 +21,7 @@
{
"customType": "regex",
"description": "singlesource: playwright npm version pinned in npx command (k8s + compose)",
"managerFilePatterns": ["^edu_master/k8s/playwright\\.yaml$", "^edu_master/compose\\.yaml$"],
"managerFilePatterns": ["edu_master/k8s/playwright.yaml", "edu_master/compose.yaml"],
"matchStrings": ["playwright@(?<currentValue>\\d+\\.\\d+\\.\\d+)"],
"datasourceTemplate": "npm",
"depNameTemplate": "playwright"
@@ -26,24 +29,42 @@
{
"customType": "regex",
"description": "singlesource: PLAYWRIGHT_VERSION file",
"managerFilePatterns": ["^edu_master/PLAYWRIGHT_VERSION$"],
"matchStrings": ["^(?<currentValue>\\d+\\.\\d+\\.\\d+)$"],
"managerFilePatterns": ["edu_master/PLAYWRIGHT_VERSION"],
"matchStrings": ["^(?<currentValue>\\d+\\.\\d+\\.\\d+)(?:\\r?\\n)?$"],
"datasourceTemplate": "pypi",
"depNameTemplate": "playwright"
},
{
"customType": "regex",
"description": "singlesource: playwright Python client version pinned in Dockerfile ARG",
"managerFilePatterns": ["edu_master/webinar-checker/Dockerfile"],
"matchStrings": ["(?:^|\\n)ARG PLAYWRIGHT_VERSION=(?<currentValue>\\d+\\.\\d+\\.\\d+)(?:\\r?\\n|$)"],
"datasourceTemplate": "pypi",
"depNameTemplate": "playwright",
"versioningTemplate": "pep440"
},
{
"customType": "regex",
"description": "kube-prometheus-stack chart version pinned in the deploy workflow",
"managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"],
"managerFilePatterns": [".gitea/workflows/deploy-lib.sh"],
"matchStrings": ["\\|prometheus-community/kube-prometheus-stack\\|prometheus\\|(?<currentValue>[0-9.]+)\\|"],
"datasourceTemplate": "helm",
"depNameTemplate": "kube-prometheus-stack",
"registryUrlTemplate": "https://prometheus-community.github.io/helm-charts"
},
{
"customType": "regex",
"description": "VictoriaMetrics Operator chart version pinned in the deploy workflow",
"managerFilePatterns": [".gitea/workflows/deploy-lib.sh"],
"matchStrings": ["\\|victoriametrics/victoria-metrics-operator\\|prometheus\\|(?<currentValue>[0-9.]+)\\|"],
"datasourceTemplate": "helm",
"depNameTemplate": "victoria-metrics-operator",
"registryUrlTemplate": "https://victoriametrics.github.io/helm-charts"
},
{
"customType": "regex",
"description": "grafana/loki chart version pinned in the deploy workflow",
"managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"],
"managerFilePatterns": [".gitea/workflows/deploy-lib.sh"],
"matchStrings": ["\\|grafana/loki\\|prometheus\\|(?<currentValue>[0-9.]+)\\|"],
"datasourceTemplate": "helm",
"depNameTemplate": "loki",
@@ -52,7 +73,7 @@
{
"customType": "regex",
"description": "grafana/alloy chart version pinned in the deploy workflow",
"managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"],
"managerFilePatterns": [".gitea/workflows/deploy-lib.sh"],
"matchStrings": ["\\|grafana/alloy\\|prometheus\\|(?<currentValue>[0-9.]+)\\|"],
"datasourceTemplate": "helm",
"depNameTemplate": "alloy",
@@ -61,7 +82,7 @@
{
"customType": "regex",
"description": "actionlint version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)ACTIONLINT_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "github-tags",
"depNameTemplate": "rhysd/actionlint"
@@ -69,7 +90,7 @@
{
"customType": "regex",
"description": "shellcheck version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)SHELLCHECK_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "github-tags",
"depNameTemplate": "koalaman/shellcheck"
@@ -77,7 +98,7 @@
{
"customType": "regex",
"description": "kubeconform version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)KUBECONFORM_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "github-tags",
"depNameTemplate": "yannh/kubeconform"
@@ -85,7 +106,7 @@
{
"customType": "regex",
"description": "uv version used to build the pytest venv",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)UV_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "github-tags",
"depNameTemplate": "astral-sh/uv"
@@ -93,7 +114,7 @@
{
"customType": "regex",
"description": "prettier version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)PRETTIER_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "npm",
"depNameTemplate": "prettier"
@@ -101,7 +122,7 @@
{
"customType": "regex",
"description": "ruff version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)RUFF_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "pypi",
"depNameTemplate": "ruff"
@@ -109,7 +130,7 @@
{
"customType": "regex",
"description": "pip-audit version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)PIP_AUDIT_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "pypi",
"depNameTemplate": "pip-audit"
@@ -117,7 +138,7 @@
{
"customType": "regex",
"description": "yamllint version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)YAMLLINT_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "pypi",
"depNameTemplate": "yamllint"
@@ -125,7 +146,7 @@
{
"customType": "regex",
"description": "hadolint version used by the ci workflow",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)HADOLINT_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "github-tags",
"depNameTemplate": "hadolint/hadolint"
@@ -133,7 +154,7 @@
{
"customType": "regex",
"description": "node version the ci workflow runs npm with",
"managerFilePatterns": ["^\\.gitea/workflows/tool-versions\\.env$"],
"managerFilePatterns": [".gitea/workflows/tool-versions.env"],
"matchStrings": ["(?:^|\\n)NODE_VERSION=\"(?<currentValue>[0-9.]+)\""],
"datasourceTemplate": "node",
"depNameTemplate": "node"
@@ -141,7 +162,7 @@
{
"customType": "regex",
"description": "stakater/reloader chart version pinned in the deploy workflow",
"managerFilePatterns": ["^\\.gitea/workflows/deploy-lib\\.sh$"],
"managerFilePatterns": [".gitea/workflows/deploy-lib.sh"],
"matchStrings": ["\\|stakater/reloader\\|reloader\\|(?<currentValue>[0-9.]+)\\|"],
"datasourceTemplate": "helm",
"depNameTemplate": "reloader",
@@ -150,7 +171,7 @@
],
"packageRules": [
{
"description": "Automerge digest and patch updates - safe by definition, review adds nothing, keeps the renovate queue and the deploy line short. Specific no-automerge rules below still override this for playwright, helm and majors.",
"description": "Automerge ordinary digest and patch updates after successful checks; specific manual-review rules below override this.",
"matchUpdateTypes": ["digest", "patch"],
"automerge": true
},
@@ -161,6 +182,12 @@
"groupSlug": "all-minor",
"automerge": false
},
{
"description": "Group ordinary patch updates; the specific groups and manual-review rules below take precedence",
"matchUpdateTypes": ["patch"],
"groupName": "all patch updates",
"groupSlug": "all-patch"
},
{
"description": "Keep private homelab images unchanged",
"matchDatasources": ["docker"],
@@ -185,7 +212,7 @@
"automerge": false
},
{
"description": "CI runs npm on the node the panel image is built from - the NODE_VERSION pin in tool-versions.env and node:22-alpine in the Dockerfile are the same dependency and move as one",
"description": "Keep CI Node runtime updates in a separate, manually reviewed group",
"matchPackageNames": ["node"],
"groupName": "node runtime",
"groupSlug": "node",
@@ -194,6 +221,8 @@
{
"description": "Helm chart bumps change PVC fields and admission behaviour, keep them reviewable",
"matchDatasources": ["helm"],
"groupName": "Helm chart {{depName}}",
"groupSlug": "helm-{{depName}}",
"automerge": false
},
{
@@ -202,12 +231,6 @@
"dependencyDashboardApproval": true,
"automerge": false
},
{
"description": "Group patch updates from all sources - automerge still applies via the digest/patch rule above (helm/playwright stay manual via their own rules)",
"matchUpdateTypes": ["patch"],
"groupName": "all patch updates",
"groupSlug": "all-patch"
},
{
"description": "Python Y-bumps break compat (3.11->3.12->3.13->3.14) - keep the base image out of the shared minor/patch groups, review every bump separately. Placed last so its groupName wins.",
"matchDatasources": ["docker"],
+2 -2
View File
@@ -12,11 +12,11 @@ services:
- "traefik.enable=true"
- "traefik.http.services.searxng.loadbalancer.server.port=8080"
# Prod Router
- "traefik.http.routers.searxng.rule=Host(`s.forust.xyz` || `search.forust.xyz`)"
- "traefik.http.routers.searxng.rule=Host(`s.forust.xyz`) || Host(`search.forust.xyz`)"
- "traefik.http.routers.searxng.entrypoints=websecure"
- "traefik.http.routers.searxng.tls.certresolver=letsencrypt"
# Local Router
- "traefik.http.routers.searxng-local.rule=Host(`s.workstation.internal` || `searxng.workstation.internal`)"
- "traefik.http.routers.searxng-local.rule=Host(`s.workstation.internal`) || Host(`searxng.workstation.internal`)"
- "traefik.http.routers.searxng-local.entrypoints=websecure"
- "traefik.http.routers.searxng-local.tls=true"
# Dev Router
+2
View File
@@ -13,6 +13,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: searxng-deployment
namespace: searxng
spec:
+1 -1
View File
@@ -19,7 +19,7 @@ services:
- streaming
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:5.2.4
image: lscr.io/linuxserver/qbittorrent:20.04.1
container_name: qbittorrent
restart: unless-stopped
environment:
+1 -1
View File
@@ -1,6 +1,6 @@
services:
termix:
image: ghcr.io/lukegus/termix:2.9.1
image: ghcr.io/lukegus/termix:2.9.2
container_name: termix
restart: unless-stopped
# ports:
+3 -1
View File
@@ -13,6 +13,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: termix-deployment
namespace: termix
spec:
@@ -29,7 +31,7 @@ spec:
spec:
containers:
- name: termix
image: ghcr.io/lukegus/termix:2.9.1
image: ghcr.io/lukegus/termix:2.9.2
envFrom:
- configMapRef:
name: termix-config
+87
View File
@@ -0,0 +1,87 @@
"""Exercise local setup and config rendering without a Docker daemon."""
import os
import shutil
import subprocess
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
class NetbirdRuntimeTests(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.root = Path(self.temp.name)
self.stack = self.root / 'netbird'
self.stack.mkdir()
for name in ('setup.sh', '.env.example', 'config.template.yaml'):
shutil.copy(ROOT / 'netbird' / name, self.stack / name)
binary = self.root / 'bin'
binary.mkdir()
docker = binary / 'docker'
docker.write_text('#!/bin/sh\nprintf "%s\\n" 172.20.0.0/16\n')
docker.chmod(0o755)
self.env = dict(os.environ, PATH=f'{binary}:{os.environ["PATH"]}')
def setup(self):
return subprocess.run( # noqa: S603 - executes the repository script copied into this test's temp dir
['/bin/bash', str(self.stack / 'setup.sh')], env=self.env, capture_output=True, check=False
)
def test_setup_preserves_existing_secrets_and_env(self):
self.assertEqual(self.setup().returncode, 0)
paths = [self.stack / '.env', *sorted((self.stack / 'secrets').iterdir())]
before = [p.read_bytes() for p in paths]
self.assertIn(b'NETBIRD_PROXY_SUBNET=172.20.0.0/16', before[0])
self.assertEqual(self.setup().returncode, 0)
self.assertEqual(before, [p.read_bytes() for p in paths])
for p in paths[1:]:
self.assertEqual(p.stat().st_mode & 0o777, 0o600)
def test_setup_rejects_empty_existing_secret(self):
(self.stack / 'secrets').mkdir()
secret = self.stack / 'secrets/datastore-encryption-key'
secret.touch()
self.assertNotEqual(self.setup().returncode, 0)
self.assertEqual(secret.read_bytes(), b'')
def render(self, subnet):
self.assertEqual(self.setup().returncode, 0)
rendered = self.root / 'run/config.yaml'
script = (ROOT / 'netbird/entrypoint.sh').read_text()
replacements = {
'/opt/netbird/config.template.yaml': str(self.stack / 'config.template.yaml'),
'/run/netbird/config.yaml': str(rendered),
'/run/secrets/relay_auth_secret': str(self.stack / 'secrets/relay-auth-secret'),
'/run/secrets/datastore_encryption_key': str(self.stack / 'secrets/datastore-encryption-key'),
'/go/bin/netbird-server': '/bin/true',
}
for original, local in replacements.items():
script = script.replace(original, local)
result = subprocess.run( # noqa: S603 - repository renderer, with test-local paths
['/bin/sh', '-c', script, 'entrypoint', '--config', str(rendered)],
env=dict(self.env, NETBIRD_DOMAIN='nb.example.com', NETBIRD_PROXY_SUBNET=subnet),
capture_output=True,
check=False,
)
return result, rendered
def test_renderer_replaces_placeholders_and_restricts_file_permissions(self):
result, rendered = self.render('172.20.0.0/16')
self.assertEqual(result.returncode, 0, result.stderr)
self.assertNotIn('__NETBIRD_', rendered.read_text())
self.assertIn('nb.example.com', rendered.read_text())
self.assertEqual(rendered.stat().st_mode & 0o777, 0o600)
def test_renderer_rejects_auto_and_default_route(self):
for subnet in ('auto', '0.0.0.0/0', '999.1.1.1/24'):
with self.subTest(subnet=subnet):
result, _ = self.render(subnet)
self.assertNotEqual(result.returncode, 0)
if __name__ == '__main__':
unittest.main()
+1 -1
View File
@@ -1,7 +1,7 @@
services:
server:
container_name: vaultwarden-server
image: vaultwarden/server:1.37.3
image: vaultwarden/server:1.37.4
restart: unless-stopped
ports:
- 9993:80
+3 -1
View File
@@ -13,6 +13,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: vaultwarden-deployment
namespace: vaultwarden
spec:
@@ -29,7 +31,7 @@ spec:
spec:
containers:
- name: vaultwarden
image: vaultwarden/server:1.37.3
image: vaultwarden/server:1.37.4
ports:
- containerPort: 80
envFrom:
+3 -1
View File
@@ -20,6 +20,8 @@ spec:
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
reloader.stakater.com/auto: "true"
name: xui-deployment
namespace: xui
spec:
@@ -36,7 +38,7 @@ spec:
spec:
containers:
- name: xui
image: ghcr.io/mhsanaei/3x-ui:v3.8.5
image: ghcr.io/mhsanaei/3x-ui:v3.9.0
envFrom:
- configMapRef:
name: xui-config