game.seasonalEvents was handed to the container unchanged, into the image's
DISABLESEASONALEVENTS. Its default true therefore wrote
DISABLESEASONALEVENTS=true: the seasonal events were OFF in every install
this chart has ever produced, while values.schema.json described the very
same key as "true means events are enabled" (#215).
Of the two ways out, this is the loud one. Inverting the template under the
old key would have flipped the behaviour of every existing release in
silence - the same class of mistake that created the issue. Renaming the key
cannot be missed: game.seasonalEvents no longer exists, additionalProperties
is false on the game block, and a values file that still carries it fails
schema validation on upgrade instead of quietly meaning the opposite.
The new key is positive (enableSeasonalEvents) to avoid a double negative,
so the chart negates it on the way into the env var. Go's "not" cannot do
that: it reads every non-empty string as true, and the schema deliberately
accepts the string form of booleans, so "false" would have flipped the wrong
way. The helper compares explicitly and fails on anything that is neither
true nor false rather than filing it under "off".
The default is enableSeasonalEvents: false, which renders the
DISABLESEASONALEVENTS this chart has always rendered. At chart defaults the
6.1.0 and 7.0.0 manifests are identical, so upgrading alone moves no server;
switching the events on is now a deliberate act with a key that means it.
Separate finding from the same issue, fixed here because it lives in the
block being touched: "--set game=null" - or a leftover "game:" whose
sub-keys were deleted - aborted the render with a nil pointer at
.Values.game.maxPlayers. The game block never got the null-safe treatment of
#99 that probes, security contexts and resources have. It now falls back to
the documented defaults, and the schema accepts the null accordingly. The
same gap remains in the dev and server blocks; it is reported, not fixed
here.
Major bump: a values key is removed, and the schema rejects it.
The publish step no longer speaks OCI. "helm registry login", "helm push"
and "helm registry logout" are replaced by a single HTTP POST of the
packaged .tgz to <charts-url>/api/charts, with Basic auth per request.
Credentials and the target URL come from a new 1Password item
op://GitHub-Actions/jlab-registry-access (username, password, charts-url).
The old item is left untouched, so a revert of this commit restores the
previous pipeline without any further work. No registry address enters
the repository.
The three properties of the old step survive:
- values are read via steps.<id>.outputs.* into the step's own env block
and used as quoted shell variables, each guarded with ":?"
- credentials stay out of argv: "curl --config -" instead of "--user"
- a failed upload fails the job: "--fail-with-body", because curl exits 0
on HTTP 401/403/409 without it and the publish would go green with
nothing uploaded
The "<chart>-*.tgz" glob is replaced by the file name built from the
chart name and the version already read from Chart.yaml; --upload-file
takes exactly one file. Two small additions fall out of measuring the
new step: the registry host is masked, because a curl connection error
prints only "<host>:<port>" and the mask on the full URL does not cover
that, and whitespace in the credentials is rejected up front, because an
unquoted curl config value ends at the first space.
No chart is touched and no chart version is bumped.
Deploys matszwe02/ytdlp_web_player, a Flask app that fetches videos with
yt-dlp, transcodes them with ffmpeg and serves them as HLS. It ships no
authentication of any kind, so the chart carries a preconfigured oauth2-proxy
as an optional subchart, in the data path in front of the app.
This is the first chart in the repo with a dependency, so the rule "charts
have no dependencies" falls here. It is replaced in CLAUDE.md and README.md by
a bar: a dependency has to be optional and off by default, upstream's chart is
consumed as it comes (documented where it falls short of our conventions,
never patched), and the price is written down - above all that a subchart is
invisible to everything downstream watching Helm release versions, so a new
proxy release only reaches a cluster when this chart is republished.
CI learns the dependency in five places, not the three the plan expected:
helm lint, package, template and install all exit 1 while charts/ is unfilled,
so ci/helm-dependencies.sh runs before lint and package in validate-charts.yml,
before package in publish-helm-chart.yml, and inside run-install-test.sh,
run-trivy-config.sh and run-quantity-render-test.sh - the last of which renders
every chart on every PR and would have gone red first.
Notable chart decisions, each measured rather than assumed:
- The image has no USER and runs as root by default, but not by necessity: it
runs under an arbitrary uid with readOnlyRootFilesystem and the full
hardening baseline. Only the pinned uid is a deviation, and it is documented.
- Two emptyDirs, each justified separately. /data holds the video cache
(DATA_PATH). /tmp is NOT mounted - measured across metadata extraction, HLS
transcoding, sprite generation and audio extraction, the image never writes
there. What it does write is $HOME/.cache, so /cache is mounted with HOME and
XDG_CACHE_HOME pointed at it; without it yt-dlp re-downloads its JavaScript
challenge-solver from github.com on every extraction.
- The proxy is wired through config.configFile, the only value the subchart
passes through tpl - config.upstreams is emitted with toJson and would carry
a literal {{ .Release.Name }} into the manifest.
- Rendering aborts when this chart's Ingress and the proxy's claim the same
host: ingress-nginx rejects the second one and a Fleet bundle then sits in
ErrApplied until forceSyncGeneration is bumped.
Fixes#214
Renovate only ever rewrites appVersion or an image tag, so every PR it
opened against a chart directory failed the build-chart version check
(#138, #139). ci/renovate-bump-chart.sh recomputes
version: <semver>+up<appVersion> as a postUpgradeTasks hook: chart major
when the app major moves, chart patch otherwise.
Each chart renders its main image as <repo>:{{ .Chart.AppVersion }}, so
repository and version live in different files and Renovate cannot join
them. The mapping is one customManager per chart in renovate.json5,
deliberately not a comment inside each Chart.yaml - that would touch all
20 chart directories and force 20 publishes for a comment change.
Patch and digest updates automerge after a 3-day minimumReleaseAge once
the pipeline is green; majors arrive one line at a time to match the
stepwise app-major procedure. ci/** stays excluded: the install-test
fixtures pin throwaway infrastructure to major lines on purpose.
Closes out the schema programme (#68) by moving what the 18 charts taught
into the reference implementation and the binding guidelines.
template-chart:
- values.schema.json as the reference schema: additionalProperties false
on every chart-owned level, pod-spec passthrough left open, and the
reasoning inline in the descriptions so a copy carries it along.
- defaultedDict and subBlock plus the per-block fallbacks for both
security contexts, the three probes and resources, wired into both the
Deployment and the StatefulSet.
- the reference itself violated guideline 1: its env block reads
ingress.domain, so with the Ingress gated off it rendered an empty
value. That read is now required(), and the comment says why - the
template is what people copy, so it has to teach the guard, not the
hole.
README, new section "Values that must not lie" with the five rules:
1. a value read outside its obvious template needs its own guard
(matrix-synapse published '{"m.server": ":443"}' to federation without
it, rendering cleanly and reporting healthy)
2. probes, contexts and resources through the null-safe helper, never
behind an "if" - a guard removes the probe instead of showing a null
3. no liveness on an external dependency where a dependency-free path
exists, otherwise document why on the helper
4. a fallback reproduces THIS chart's defaults, not the repo standard -
calibre's readOnlyRootFilesystem: false is what keeps the runtime uid
at 1000 instead of 911 and existing volume data readable
5. a probe default carries its context - port name, Host header, exec
command - or the probe vanishes (teamspeak) or gets 403 (patchmon)
Plus the two mechanics that are easiest to lose: null reaches schema
validation only for keys WITHOUT a chart default (which is why the helper
and the ["object","null"] types each cover a case the other cannot), and
schemas must be validated against real bundle values - that step caught a
draft of mine that would have broken three live paperless releases.
CLAUDE.md: the new-chart checklist now names the schema and the
adaptation step, since the copied file is chart-specific and wrong until
it is adapted.
freesailarr is not covered: it is not on main yet (#59).
A changed ConfigMap did not restart any pod in this repo: no chart carries
a checksum/config annotation, so the pod template stays identical and the
running container keeps its old config. With subPath mounts (the usual form
here) the kubelet never refreshes the file either, so the change is silently
ineffective - see #82, where this caused a real outage while every bundle was
green and the pod Ready.
template-chart is the reference implementation, so it gets the annotation on
both pod templates (Deployment and StatefulSet), and the README style guide
gets a new binding section 9 covering the rule, the reason, and the limit for
1Password-backed Secrets.
Major bump: runtime behavior changes - a config change now costs a restart.
README.md gains a comprehensive 'Chart style guidelines' section - the
binding end state of the conventions overhaul (#32, batches 0-4) for all
current and future charts: naming (incl. the PVC-name exception and the
StatefulSet migration pattern), versioning scheme and semver rules,
image pinning, the ingress standard (domain + clusterIssuer strictly
required without defaults, className default nginx, annotations merge,
TLS secret name, homeassistant exception), security hardening with
mandatory documentation of deviations, probes, the auto-limits scheme,
the secrets.<name>SecretRef convention, tpl-rendered env blocks,
scaling, and the icon/lint/CI-fixture/no-dependencies rules.
The Versioning section now points there and documents that app-version
updates go through PRs. CLAUDE.md's conventions section is aligned as a
compact summary referencing the README guide.
Establish the flexible ingress standard from #32 in template-chart and
document it in README/CLAUDE.md: ingress.domain and ingress.clusterIssuer
stay required, new ingress.className value (default nginx) replaces the
hardcoded ingressClassName, new optional ingress.annotations are merged
with the cert-manager cluster-issuer annotation, TLS secret name
unchanged (tls-<release>-<chart>-ingress).
Apply the standard to cyberchef and patchmon (the two charts without an
own batch-3 major PR) and make the patchmon probes values-configurable
(server + guacd, rendered through tpl so the default Host header keeps
following ingress.domain). Defaults render semantically identical to the
previous hardcoded output.
cyberchef 5.0.1 -> 5.1.0, patchmon 5.0.1 -> 5.1.0 (minor, additive).
- validation happens via strict lint, PR pipeline and install-test stage
- new charts need ci/ fixtures or an exclusion entry
- codify semver conventions (patch/minor/major)
- app updates via PRs with stepped major upgrades
Add securityContext hardening (runAsNonRoot, readOnlyRootFilesystem,
capabilities drop ALL, allowPrivilegeEscalation false) to the 10 workloads
that lacked it: apache-tika, cyberchef, gotenberg, homeassistant,
matrix-delegation-nginx, paperless-server, paperless-consume cronjob
(plus automountServiceAccountToken false), patchmon-frontend,
urlaubsverwaltung and zipline. All contexts are configurable via
securityContext.pod / securityContext.container in values.yaml; writable
paths are provided as emptyDirs. Image-forced deviations (Home Assistant
root start for s6-overlay + NET_RAW for discovery, paperless root init
container for /run ownership) are documented in the charts' values.yaml.
Major version bump for every changed published chart.