Maintenance¶
Two things keep the repository from rotting: GitHub Actions workflows that lint everything, and Renovate, which chases dependencies.
CI workflows¶
All in .github/workflows/.
| Workflow | Trigger | What it checks |
|---|---|---|
lint-ansible.yaml |
ansible/** |
ansible-lint |
lint-python.yaml |
boot_server/*.py, pyproject.toml, uv.lock |
pylint on the boot server |
lint-yaml.yaml |
**/*.yaml |
yamllint |
lint-markdown.yaml |
**/*.md |
markdownlint-cli2 |
docs.yaml |
push to main under docs/**, overrides/**, zensical.toml, pyproject.toml, uv.lock |
Builds the site with --strict and publishes it — see Contributing |
preview.yaml |
pull requests on the same paths | Publishes a preview under pr-preview/; fork PRs skipped |
argo-diff-preview.yaml |
pull requests under payload/** |
Comments the rendered ArgoCD manifest diff against main; fork PRs skipped |
image-scan.yaml |
pull requests under payload/** |
scripts/image-scan-diff.py: renders every Application on main and on the PR, scans with Trivy only the images that changed, and fails on a fixable CRITICAL the replaced image did not carry. A finding already waiting on a release does not block the bump that gets closer to it |
renovate-validate.yaml |
renovate.json |
renovate-config-validator |
fonts-check.yaml |
scripts/update-fonts.sh, docs/assets/fonts/** |
make fonts-check: the committed woff2 files match the pinned releases — see Fonts |
Renovate¶
Configured in renovate.json. It runs at any time with no hourly or
concurrency limit and no grouping: every component gets its own pull request,
so a failing update never holds up an unrelated one.
Automerge policy¶
- Patch and minor updates automerge; majors wait for a human. One rule for everything, with no per-area exceptions: a Flatcar or Kubernetes minor lands the same way a Grafana chart patch does.
- Three days between a release and its PR for images and charts
(
minimumReleaseAge): long enough for an upstream to pull a broken or compromised tag, short enough that a fix arrives the same week. Renovate lifts it for its own vulnerability-alert PRs. prometheus-operator-crdsmust not lagkube-prometheus-stack. Merge the CRD bump first, or both together.- Flatcar, Kubernetes and containerd bumps change what a newly provisioned node installs, not what a running node runs. kubeadm cannot skip a minor, so a cluster left unrebuilt across two automerged Kubernetes minors has to be walked forward one at a time — see Upgrades.
- Font bumps automerge without their second commit unless
fonts-check.yamlis a required check onmain. If branch protection is ever rebuilt, put it back — see Fonts. config:best-practicespins GitHub Actions to commit SHAs and container images to digests, and collects every pin into one sharedrenovate/pin-dependenciesbranch.
Scope¶
Python (pyproject.toml, uv.lock), Docker images, GitHub Actions, Kubernetes
and ArgoCD manifests, Helm values files (payload/**/values.yaml) and
pre-commit hooks, plus custom regex managers for the versions in
ansible/inventory.yaml, the font pins in scripts/update-fonts.sh, and any
# renovate: annotation under payload/. The Makefile is deliberately not
tracked: the bootstrap targets read targetRevision out of the owning
application.yaml — see Version pins.
Manager rules¶
A misconfigured manager fails silently: the run succeeds and nothing is checked. Prove a manager works by reading what it extracts:
It needs no credentials for a public repository and prints every dependency it
found and why it skipped any. The rules, each of which is written into
renovate.json:
pre-commitmust be enabled explicitly ("pre-commit": {"enabled": true}); Renovate ships that manager disabled, and apackageRulesentry for it does nothing on its own.- The
helm-valuesmanager reads only real values files. An image tag inline in an Application'shelm.valuesObject:is invisible to it; put a# renovate: datasource=... depName=...comment on the line above so the annotation manager picks it up, or move the values into avalues.yaml. - A custom manager that pins a full image reference needs
autoReplaceStringTemplate. Without it the pin cannot be written, and one unwritable pin fails the whole shared pin branch (Error updating branch: update failureon the Dependency Dashboard, the only symptom, since version bumps keep working). CapturingcurrentDigestonly lets Renovate update a digest that is already there. - A bare Helm
tag:value goes in thepinDigests: falserule. A tag like3.4.4-alpinehas no position for a digest; leave one out and the same banner comes back. - A two-part version needs a
versioningTemplate. The defaultsemver-coercedrejects6.03, so the dependency is extracted and then dropped before lookup.syslinuxand the Inter font pin carry aregex:versioning for this reason; Inter's also keepsv4.0-beta9h-style prereleases out. - Match the registry's spelling, not the GitHub org's. The diff-preview
image is
dagandersen/argocd-diff-previewon Docker Hub; a regex ondag-andersenmatches nothing and reports nothing. - Hold a major with a regex, not a range.
"allowedVersions": "/^17\\./"is matched before version parsing; a range like<18is graded by npm semver, where a three-part tag such as17.11.1-trixieparses as a prerelease and is dropped from every range that does not name one. - Check that the currently pinned version passes its own rule. A filter that excludes what is deployed excludes everything, silently.