24 Commits
Author SHA1 Message Date
jklein 055d9f6a31 Merge pull request #240 from jk-cluster/ci/exclude-charts-from-install-matrix
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Failing after 6s
Publish Minecraft-Router Helm Chart / Publish Minecraft-Router Helm Chart (push) Failing after 6s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Failing after 5s
Publish Outline Helm Chart / Publish Outline Helm Chart (push) Failing after 5s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Failing after 5s
Publish Rallly Helm Chart / Publish Rallly Helm Chart (push) Failing after 5s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Failing after 5s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Failing after 5s
Publish SearXNG Helm Chart / Publish SearXNG Helm Chart (push) Failing after 5s
Publish Urlaubsverwaltung Helm Chart / Publish Urlaubsverwaltung Helm Chart (push) Failing after 5s
Publish Helm Chart with patched Application Version / publish-helm-chart (push) Successful in 6s
Publish VoucherVault Helm Chart / Publish VoucherVault Helm Chart (push) Successful in 7s
ci: ausgeschlossene Charts aus der Install-Test-Matrix filtern
2026-09-14 11:04:43 +00:00
jklein 1009738c15 Merge pull request #248 from jk-cluster/renovate/docker.io-searxng-searxng-2026.x
Update docker.io/searxng/searxng Docker tag to v2026.9.11-ffe96f8a6
2026-09-14 11:03:48 +00:00
jklein 9f8b421b64 Merge pull request #249 from jk-cluster/renovate/outlinewiki-outline-1.x
Update outlinewiki/outline Docker tag to v1.10.1
2026-09-14 11:03:30 +00:00
jklein 3def619306 Merge pull request #250 from jk-cluster/renovate/urlaubsverwaltung-urlaubsverwaltung-6.x
Update urlaubsverwaltung/urlaubsverwaltung Docker tag to v6.12.0
2026-09-14 11:03:17 +00:00
jklein b020532942 Merge pull request #251 from jk-cluster/renovate/lukevella-rallly-4.x
Update lukevella/rallly Docker tag to v4.15.0
2026-09-14 11:03:03 +00:00
jlab-renovate[bot] 1fa5179813 Update docker.io/searxng/searxng Docker tag to v2026.9.11-ffe96f8a6 2026-09-14 07:02:09 +00:00
jlab-renovate[bot] eb6e40d9a0 Update urlaubsverwaltung/urlaubsverwaltung Docker tag to v6.12.0 2026-09-13 14:02:39 +00:00
jlab-renovate[bot] 20c2bf9d97 Update lukevella/rallly Docker tag to v4.15.0 2026-09-12 17:02:22 +00:00
jlab-renovate[bot] a446454900 Update outlinewiki/outline Docker tag to v1.10.1 2026-09-12 14:02:07 +00:00
jklein 49ebd4be3c Merge pull request #243 from jk-cluster/renovate/lukevella-rallly-4.x
Update lukevella/rallly Docker tag to v4.14.0
2026-09-08 21:40:47 +00:00
jklein be5ebdbe07 Merge pull request #244 from jk-cluster/renovate/itzg-mc-router-1.x
Update itzg/mc-router Docker tag to v1.46.5
2026-09-08 21:40:39 +00:00
jklein 7c31ab45c5 Merge pull request #234 from jk-cluster/renovate/urlaubsverwaltung-urlaubsverwaltung-6.x
Update urlaubsverwaltung/urlaubsverwaltung Docker tag to v6.10.0
2026-09-08 21:40:24 +00:00
jklein 63a4783d55 Merge pull request #242 from jk-cluster/renovate/outlinewiki-outline-1.x
Update outlinewiki/outline Docker tag to v1.10.0
2026-09-08 21:40:11 +00:00
jlab-renovate[bot] e0c2de883c Update urlaubsverwaltung/urlaubsverwaltung Docker tag to v6.10.0 2026-09-08 21:34:41 +00:00
jlab-renovate[bot] 706dee7458 Update outlinewiki/outline Docker tag to v1.10.0 2026-09-08 21:34:30 +00:00
jlab-renovate[bot] 14412a4670 Update lukevella/rallly Docker tag to v4.14.0 2026-09-08 21:34:19 +00:00
jklein 2b7c24be09 Merge pull request #236 from jk-cluster/renovate/docker.io-searxng-searxng-2026.x
Update docker.io/searxng/searxng Docker tag to v2026.9.6-8b01679e8
2026-09-08 21:34:05 +00:00
jlab-renovate[bot] 9c0e1e6306 Update itzg/mc-router Docker tag to v1.46.5 2026-09-08 21:33:57 +00:00
jklein 024e4e9de1 Merge pull request #235 from jk-cluster/renovate/l4rm4nd-vouchervault-1.x
Update l4rm4nd/vouchervault Docker tag to v1.30.2
2026-09-08 21:32:27 +00:00
jlab-renovate[bot] b9a2e3daa3 Update docker.io/searxng/searxng Docker tag to v2026.9.6-8b01679e8 2026-09-08 18:02:03 +00:00
jklein 5a4eeefd0e docs: the excluded-chart signal, and minecraft-server's image-tag exception
Two documentation gaps, both about a reader drawing the wrong conclusion.

The install-test section said nothing about what an exclusion looks like in a PR's check list. It now says that an excluded chart gets no job at all rather than a green tick, that the detect job names it with its documented reason in the summary, that the mandatory justification is what makes a line an exclusion in the first place, and why the per-chart "if" that would produce a visible Skipped is not available (#231).

Section 3 of the style guidelines lists the charts that deviate from "the main image tag comes from appVersion" so that nobody mistakes a deviation for a mistake and tidies it away. minecraft-server has been the only chart with an overridable tag since #158 and was missing from that list: the line says that image.tagOverride exists, why (only the newest Java line is maintained, so the line becomes a decision at rollout time instead of a property of the chart), and that the chart checks the pairing of effective tag and minecraft.version itself and fails the render on a mismatch (#232).
2026-09-04 16:58:22 +02:00
jklein 622e4c3ff8 ci: drop excluded charts from the install-test matrix
A chart in ci/excluded-charts.txt got an install-test job that ran the script, printed SKIP and exited 0. GitHub knows only the exit code, so the check list showed "Install-test satisfactory OK" for a chart whose image was never pulled - indistinguishable from one that really came up in the cluster (#231). detect-changed-charts now removes those charts before the matrix is built, so no job exists for them and nothing claims they passed.

An absence says nothing by itself, so the detect job writes every excluded chart of the PR with its documented justification into the job summary. A "Skipped" would only say "not run"; the reason is the question a review actually asks.

The list is not parsed in YAML. The workflow calls ci/run-install-test.sh --list-excluded, the one parser, and compares whole strings with jq, so a chart name is never read as a pattern. The script's own exclusion branch now goes through the same excluded_reason() helper instead of a grep that used the chart name as a regex; it stays for local runs, where the CI filter does not apply.

Not viable, and recorded at the matrix as a comment: deciding per chart in an "if" on the install-test job, which would give a visible Skipped entry instead of an absent one. GitHub rejects it - jobs.<id>.if is evaluated before the matrix is expanded, so the matrix context does not exist there ("context \"matrix\" is not allowed here. available contexts are \"github\", \"inputs\", \"needs\", \"vars\"."). Run 33650724109 on the closed PR #233 ended in failure with zero jobs created.
2026-09-04 16:58:14 +02:00
jklein 7e168da492 ci: parse the exclusion list in exactly one place
Two consumers read ci/excluded-charts.txt: this script and the install-test matrix in validate-charts.yml, which has to drop excluded charts before the matrix is built (#231). Two parsers would be two chances to select different charts, and the disagreement would be silent in either direction. So the script grows a --list-excluded mode and the workflow will call it instead of re-implementing the parsing.

A line carrying only a chart name and no justification is deliberately not an exclusion: the README requires the reason, and a bare name would drop a chart out of CI with nothing to review. The reason lookup matches the name exactly instead of as a regex, so a chart name is never read as a pattern.

The workflow side is not part of this commit.
2026-09-04 16:52:08 +02:00
jlab-renovate[bot] 9f1c776672 Update l4rm4nd/vouchervault Docker tag to v1.30.2 2026-09-03 22:01:42 +00:00
9 changed files with 134 additions and 25 deletions

No files matched your search

+72 -7
View File
@@ -11,6 +11,8 @@ jobs:
any-chart-changed: ${{ steps.detect.outputs.any-chart-changed }} any-chart-changed: ${{ steps.detect.outputs.any-chart-changed }}
scan-charts: ${{ steps.detect.outputs.scan-charts }} scan-charts: ${{ steps.detect.outputs.scan-charts }}
any-scan: ${{ steps.detect.outputs.any-scan }} any-scan: ${{ steps.detect.outputs.any-scan }}
install-charts: ${{ steps.detect.outputs.install-charts }}
any-install: ${{ steps.detect.outputs.any-install }}
steps: steps:
- name: "Checkout Repository" - name: "Checkout Repository"
uses: actions/checkout@v7 uses: actions/checkout@v7
@@ -54,6 +56,55 @@ jobs:
echo "any-scan=true" >> "$GITHUB_OUTPUT" echo "any-scan=true" >> "$GITHUB_OUTPUT"
fi fi
# --- The install-test matrix: changed charts minus the excluded ones
# (issue #231). A chart in ci/excluded-charts.txt used to get a job
# that ran the script, printed SKIP and exited 0 - and GitHub knows
# only the exit code, so the check list showed a green tick for a
# chart nothing was ever attempted on. Dropping it here means no job
# exists for it and nothing claims it passed.
#
# The list is NOT parsed here. ci/run-install-test.sh --list-excluded
# emits "<chart><TAB><reason>", and that script is the one parser;
# re-implementing the format in YAML would be a second chance to
# select different charts, silently. The comparison below is jq's
# "!=" on whole strings, so a chart name is never read as a pattern.
INSTALL_CHARTS="$CHARTS"
SUMMARY_ROWS=""
while IFS=$'\t' read -r EX_NAME EX_REASON; do
[ -n "$EX_NAME" ] || continue
if [ "$(echo "$INSTALL_CHARTS" | jq -r --arg c "$EX_NAME" 'index($c) != null')" = "true" ]; then
# A "|" in the justification would break out of the table cell.
SUMMARY_ROWS="${SUMMARY_ROWS}| \`$EX_NAME\` | ${EX_REASON//|/\\|} |"$'\n'
echo "Excluded from the install-test: $EX_NAME - $EX_REASON"
fi
INSTALL_CHARTS="$(echo "$INSTALL_CHARTS" | jq -c --arg c "$EX_NAME" 'map(select(. != $c))')"
done <<< "$(ci/run-install-test.sh --list-excluded)"
echo "install-charts=$INSTALL_CHARTS" >> "$GITHUB_OUTPUT"
if [ "$INSTALL_CHARTS" = "[]" ]; then
echo "any-install=false" >> "$GITHUB_OUTPUT"
else
echo "any-install=true" >> "$GITHUB_OUTPUT"
fi
# An absent job says "not run" but never why, and an absence is easy
# to miss entirely. So every excluded chart of this PR is named here
# WITH its documented justification - which is the question a review
# actually asks. This summary is the visible half of the change; the
# dropped matrix entry is only the half that stops the lie.
if [ -n "$SUMMARY_ROWS" ]; then
{
echo "### Charts excluded from the install-test"
echo
echo "These charts are changed by this PR but listed in \`ci/excluded-charts.txt\`, so they get **no** install-test job. There is no green tick for them, because nothing was attempted (issue #231)."
echo
echo "| Chart | Documented reason |"
echo "| --- | --- |"
printf '%s' "$SUMMARY_ROWS"
echo
} >> "$GITHUB_STEP_SUMMARY"
fi
echo "Install-test matrix: $INSTALL_CHARTS"
build-chart: build-chart:
name: "Build ${{ matrix.chart }}" name: "Build ${{ matrix.chart }}"
needs: detect-changed-charts needs: detect-changed-charts
@@ -162,7 +213,7 @@ jobs:
install-test: install-test:
name: "Install-test ${{ matrix.chart }}" name: "Install-test ${{ matrix.chart }}"
needs: [detect-changed-charts, build-chart] needs: [detect-changed-charts, build-chart]
if: needs.detect-changed-charts.outputs.any-chart-changed == 'true' if: needs.detect-changed-charts.outputs.any-install == 'true'
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Blocking since Renovate automerges patch/minor updates: a chart that no # Blocking since Renovate automerges patch/minor updates: a chart that no
# longer starts must stop the PR before it reaches main and publishes. This # longer starts must stop the PR before it reaches main and publishes. This
@@ -171,8 +222,22 @@ jobs:
timeout-minutes: 30 timeout-minutes: 30
strategy: strategy:
fail-fast: false fail-fast: false
# "install-charts", not "charts": the changed charts minus the ones in
# ci/excluded-charts.txt, filtered in detect-changed-charts. Excluded
# charts are named with their reason in that job's summary (issue #231).
#
# DO NOT try to move that decision into an "if" on this job, e.g.
# if: ... && !contains(fromJSON(needs.detect-changed-charts.outputs.excluded-charts), matrix.chart)
# to get a visible "Skipped" per chart instead of an absent entry.
# GitHub rejects it - jobs.<id>.if is evaluated BEFORE the matrix is
# expanded, so the matrix context does not exist there:
# context "matrix" is not allowed here.
# available contexts are "github", "inputs", "needs", "vars".
# Measured on run 33650724109 (PR #233, closed): the whole workflow ended
# in failure with zero jobs created. The per-chart value has to be gone
# from the matrix by the time this job is reached.
matrix: matrix:
chart: ${{ fromJSON(needs.detect-changed-charts.outputs.charts) }} chart: ${{ fromJSON(needs.detect-changed-charts.outputs.install-charts) }}
env: env:
K3D_VERSION: v5.9.0 K3D_VERSION: v5.9.0
K3S_IMAGE: docker.io/rancher/k3s:v1.35.5-k3s1 K3S_IMAGE: docker.io/rancher/k3s:v1.35.5-k3s1
@@ -227,11 +292,11 @@ jobs:
run: curl -sSfL https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | TAG="$K3D_VERSION" bash run: curl -sSfL https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | TAG="$K3D_VERSION" bash
- name: "Create k3d cluster" - name: "Create k3d cluster"
run: k3d cluster create install-test --image "$K3S_IMAGE" --wait --timeout 180s run: k3d cluster create install-test --image "$K3S_IMAGE" --wait --timeout 180s
# Excluded charts (see ci/excluded-charts.txt) are skipped inside the # Every chart reaching this step is one the exclusion filter let through,
# script with their documented reason. The variables below are empty for # so the script's SKIP branch is never taken here - it exists for local
# every other chart and are read only by the chart that declares them in # runs. The variables below are empty for every chart but the one that
# ci/<chart>/settings.env (REQUIRED_ENV); the script fails loudly if a # declares them in ci/<chart>/settings.env (REQUIRED_ENV); the script
# declared one is missing, it never skips over it. # fails loudly if a declared one is missing, it never skips over it.
- name: "Install chart and wait for readiness" - name: "Install chart and wait for readiness"
env: env:
FREESAILARR_WG_KEY: ${{ steps.load-freesailarr-vpn.outputs.FREESAILARR_WG_KEY }} FREESAILARR_WG_KEY: ${{ steps.load-freesailarr-vpn.outputs.FREESAILARR_WG_KEY }}
+5 -2
View File
@@ -49,7 +49,7 @@ Collection of some self-made helm-charts. Each top-level directory is a standalo
Note: the registry **accepts a re-upload of an already published version** (HTTP 201) and replaces the stored artifact, so a republish of an existing version is still silent — see #160. Note: the registry **accepts a re-upload of an already published version** (HTTP 201) and replaces the stored artifact, so a republish of an existing version is still silent — see #160.
- `change-app-version.yml` — manually dispatched app-version bump; the chart must be listed in its `chart-name` choice list. - `change-app-version.yml` — manually dispatched app-version bump; the chart must be listed in its `chart-name` choice list.
- `ci/helm-dependencies.sh <chart-dir>` — not a workflow but the step they all share: fetches a chart's declared dependencies into `charts/`, and returns silently for the charts that declare none (all but one). It runs in **five** places, because every helm command this repo builds on exits 1 while a declared dependency is missing: before lint and package in `validate-charts.yml`, before package in `publish-helm-chart.yml`, and inside `ci/run-install-test.sh`, `ci/run-quantity-render-test.sh` and `ci/run-trivy-config.sh`. See "Chart dependencies" in the style guidelines for what that costs. - `ci/helm-dependencies.sh <chart-dir>` — not a workflow but the step they all share: fetches a chart's declared dependencies into `charts/`, and returns silently for the charts that declare none (all but one). It runs in **five** places, because every helm command this repo builds on exits 1 while a declared dependency is missing: before lint and package in `validate-charts.yml`, before package in `publish-helm-chart.yml`, and inside `ci/run-install-test.sh`, `ci/run-quantity-render-test.sh` and `ci/run-trivy-config.sh`. See "Chart dependencies" in the style guidelines for what that costs.
- `validate-charts.yml` — PR pipeline: detects which charts a PR touches and, for exactly those, runs `helm lint --strict`, `helm package`, and a chart-version bump check (`template-chart` is exempt from the version check). PRs without chart changes skip the build matrix. After the build stage, an **install-test** job (same changed-charts matrix) installs each changed chart into an ephemeral [k3d](https://k3d.io) cluster on the runner and requires every workload to become Ready — see below. Both matrices follow the changed chart directories, so a PR that only changes the harness under `ci/` exercises nothing by itself; the Trivy scan has a fixed-sample fallback for that case, the install-test has none — bump the affected chart's patch version to pull it into the matrix. The **resource-quantity render test** is the exception to that rule: it runs over every chart on every PR — see below. - `validate-charts.yml` — PR pipeline: detects which charts a PR touches and, for exactly those, runs `helm lint --strict`, `helm package`, and a chart-version bump check (`template-chart` is exempt from the version check). PRs without chart changes skip the build matrix. After the build stage, an **install-test** job (the same changed-charts matrix, minus the charts listed in `ci/excluded-charts.txt`) installs each changed chart into an ephemeral [k3d](https://k3d.io) cluster on the runner and requires every workload to become Ready — see below. Both matrices follow the changed chart directories, so a PR that only changes the harness under `ci/` exercises nothing by itself; the Trivy scan has a fixed-sample fallback for that case, the install-test has none — bump the affected chart's patch version to pull it into the matrix. The **resource-quantity render test** is the exception to that rule: it runs over every chart on every PR — see below.
### Resource-quantity render test (all charts, every PR) ### Resource-quantity render test (all charts, every PR)
@@ -80,10 +80,12 @@ Fixtures live at the **repo root** under `ci/` (deliberately *not* inside the ch
- `ci/<chart>/fixtures.yaml` — dummy secrets with in-cluster connection data, throwaway infrastructure (postgres/redis/dex), static hostPath PVs/StorageClasses where the chart pins a storage class or needs RWX, and seed jobs (e.g. `synapse generate`) - `ci/<chart>/fixtures.yaml` — dummy secrets with in-cluster connection data, throwaway infrastructure (postgres/redis/dex), static hostPath PVs/StorageClasses where the chart pins a storage class or needs RWX, and seed jobs (e.g. `synapse generate`)
- `ci/<chart>/fixtures.env.yaml` — fixtures that need a value which must not be committed. The file holds `${VARIABLE}` placeholders and nothing else; `ci/run-install-test.sh` renders it with `envsubst` and pipes the result straight into `kubectl` (never printed). Substitution is restricted to the names from `REQUIRED_ENV`, so an undeclared variable is never filled in and no unrelated `$…` in the manifest is touched - `ci/<chart>/fixtures.env.yaml` — fixtures that need a value which must not be committed. The file holds `${VARIABLE}` placeholders and nothing else; `ci/run-install-test.sh` renders it with `envsubst` and pipes the result straight into `kubectl` (never printed). Substitution is restricted to the names from `REQUIRED_ENV`, so an undeclared variable is never filled in and no unrelated `$…` in the manifest is touched
- `ci/<chart>/settings.env` — optional overrides: `INSTALL_TIMEOUT` for slow starters, and `REQUIRED_ENV="VAR_A VAR_B"` to declare the variables `fixtures.env.yaml` needs. **A missing or empty declared variable fails the install-test loudly, naming the variable — it never skips the chart.** Skipping would restore exactly the green-but-meaningless signal this mechanism exists to remove; the one legitimate case of absent secrets, a PR from a fork, belongs in an `if` on the workflow job rather than in the script - `ci/<chart>/settings.env` — optional overrides: `INSTALL_TIMEOUT` for slow starters, and `REQUIRED_ENV="VAR_A VAR_B"` to declare the variables `fixtures.env.yaml` needs. **A missing or empty declared variable fails the install-test loudly, naming the variable — it never skips the chart.** Skipping would restore exactly the green-but-meaningless signal this mechanism exists to remove; the one legitimate case of absent secrets, a PR from a fork, belongs in an `if` on the workflow job rather than in the script
- `ci/excluded-charts.txt` — charts that cannot run in CI, each with a justification. Currently: `satisfactory` (multi-GB image + 10GB+ Steam download), `template-chart` (scaffold with placeholder image) - `ci/excluded-charts.txt` — charts that cannot run in CI, one per line as `<chart> <justification>`. Currently: `satisfactory` (multi-GB image + 10GB+ Steam download), `template-chart` (scaffold with placeholder image). The justification is mandatory: a line carrying only a chart name is deliberately **not** an exclusion, because a bare name would drop a chart out of CI with nothing to review. **An excluded chart gets no install-test job at all** — see below
`freesailarr` is the only chart using `fixtures.env.yaml` today: gluetun needs a real Wireguard key, or it never completes a handshake and no app container behind its kill switch becomes Ready. The install-test job loads the key from 1Password Connect for that chart only, using the same pattern as `publish-helm-chart.yml` — `steps.<id>.outputs.*` into the step's own `env:`, quoted, with `:?` guards that name the missing `op://` field before the cluster is even created. The key in that item is a **CI-only Mullvad key**, deliberately not the production one: Mullvad ties an address to a key and routes the tunnel to the endpoint it saw last, so a shared key would make every PR run pull the tunnel out from under the running stack — with the symptom in the cluster and the cause in a pull request. `freesailarr` is the only chart using `fixtures.env.yaml` today: gluetun needs a real Wireguard key, or it never completes a handshake and no app container behind its kill switch becomes Ready. The install-test job loads the key from 1Password Connect for that chart only, using the same pattern as `publish-helm-chart.yml` — `steps.<id>.outputs.*` into the step's own `env:`, quoted, with `:?` guards that name the missing `op://` field before the cluster is even created. The key in that item is a **CI-only Mullvad key**, deliberately not the production one: Mullvad ties an address to a key and routes the tunnel to the endpoint it saw last, so a shared key would make every PR run pull the tunnel out from under the running stack — with the symptom in the cluster and the cause in a pull request.
**An excluded chart is dropped from the matrix, not skipped inside its job (#231).** It used to get a job that ran the script, printed `SKIP` and exited 0 — and GitHub knows only the exit code, so the check list showed `Install-test satisfactory ✓` for a chart whose image was never pulled, indistinguishable from one that really came up in the cluster. `detect-changed-charts` now removes the excluded charts before the matrix is built, so no job exists for them and nothing claims they passed. Because an absence on its own says nothing, that job writes every excluded chart of the PR **with its documented justification** into the job summary — a `Skipped` would only say "not run", and why is the question a review actually asks. The list is parsed in exactly one place: `ci/run-install-test.sh --list-excluded` emits `<chart><TAB><reason>` and the workflow calls it instead of re-implementing the format in YAML, since a second parser would be a second chance to select different charts — silently, and in either direction. The script keeps its own `SKIP` branch for local runs. What cannot be done instead: deciding per chart in an `if` on the install-test job, to get a visible `Skipped` entry rather than an absent one. GitHub rejects it with `context "matrix" is not allowed here` — `jobs.<id>.if` is evaluated **before** the matrix is expanded, so the `matrix` context does not exist there, and the whole workflow ends in failure with zero jobs created (measured on the closed PR #233).
Fixture images avoid Docker Hub where possible (`public.ecr.aws`/`ghcr.io`) to dodge runner rate limits. The job **blocks the PR** on failure — the stabilization phase of #22 ended when Renovate started automerging patch/minor updates, since a chart that no longer starts must not reach `main` and publish unattended. When adding a new chart, add its `ci/<chart>/` fixtures (or an entry in `ci/excluded-charts.txt`) — the install-test fails if both are missing. The script also runs locally against any existing cluster: `k3d cluster create t && ci/run-install-test.sh <chart> && k3d cluster delete t`. Fixture images avoid Docker Hub where possible (`public.ecr.aws`/`ghcr.io`) to dodge runner rate limits. The job **blocks the PR** on failure — the stabilization phase of #22 ended when Renovate started automerging patch/minor updates, since a chart that no longer starts must not reach `main` and publish unattended. When adding a new chart, add its `ci/<chart>/` fixtures (or an entry in `ci/excluded-charts.txt`) — the install-test fails if both are missing. The script also runs locally against any existing cluster: `k3d cluster create t && ci/run-install-test.sh <chart> && k3d cluster delete t`.
## Dependency updates (Renovate) ## Dependency updates (Renovate)
@@ -182,6 +184,7 @@ These guidelines are **binding for every current and future chart** in this repo
- The main app image tag comes from `{{ .Chart.AppVersion }}` — never hardcoded. - The main app image tag comes from `{{ .Chart.AppVersion }}` — never hardcoded.
- **Documented exception — freesailarr**: the chart bundles nine independently versioned upstreams, so none of them is "the" app version. **No** tag comes from `appVersion`; all nine images are pinned as `<app>.image.repository` / `<app>.image.tag` in `values.yaml`, which is also what puts every one of them under the built-in `helm-values` manager. Its `appVersion` is the chart's own version marker, freely chosen and tied to no upstream — raised by hand when a new edition of the chart is marked as a whole. Consequently the chart has **no** block in `renovate.json5` and is **not** in the `change-app-version.yml` choice list. - **Documented exception — freesailarr**: the chart bundles nine independently versioned upstreams, so none of them is "the" app version. **No** tag comes from `appVersion`; all nine images are pinned as `<app>.image.repository` / `<app>.image.tag` in `values.yaml`, which is also what puts every one of them under the built-in `helm-values` manager. Its `appVersion` is the chart's own version marker, freely chosen and tied to no upstream — raised by hand when a new edition of the chart is marked as a whole. Consequently the chart has **no** block in `renovate.json5` and is **not** in the `change-app-version.yml` choice list.
- **Documented exception — minecraft-server**: the only chart whose main image tag can be overridden, through `image.tagOverride` (#158, empty by default — the tag still comes from `appVersion`). `appVersion` is the tag and therefore fixes the JVM, and since chart 5.0.0 only the **newest** Java line is maintained out of this one directory; the older lines stay published as frozen chart versions (java8 and java17 at 4.0.0, java21 at 4.1.0) and never receive another change. Without the override, running an older JVM would mean installing a frozen chart and giving up every later fix — so the line is a decision at rollout time rather than a property of the chart. The override is named `tagOverride`, not `tag`, because it decouples the running image from the chart version, and it is deliberately outside Renovate's reach (`renovate.json5` tracks the `appVersion` in `Chart.yaml` and nothing else). The chart checks the pairing itself: the Java line of the **effective** tag against `minecraft.version`, failing the render with both values named — a mismatch used to install silently and only break the container afterwards.
- Auxiliary images (sidecars, init containers, secondary workloads — e.g. the delegation nginx in matrix-synapse, guacd in patchmon, busybox init/consume images in paperless-ngx) are pinned via values instead of being hardcoded in templates, and their pinned versions are reviewed alongside every app-version update round. - Auxiliary images (sidecars, init containers, secondary workloads — e.g. the delegation nginx in matrix-synapse, guacd in patchmon, busybox init/consume images in paperless-ngx) are pinned via values instead of being hardcoded in templates, and their pinned versions are reviewed alongside every app-version update round.
### 4. Ingress ### 4. Ingress
+45 -4
View File
@@ -4,6 +4,7 @@
# fixtures under ci/<chart>/ and requires every workload to become Ready. # fixtures under ci/<chart>/ and requires every workload to become Ready.
# #
# Usage: ci/run-install-test.sh <chart-name> # Usage: ci/run-install-test.sh <chart-name>
# ci/run-install-test.sh --list-excluded
# #
# Layout (repo root): # Layout (repo root):
# ci/common/ fixtures applied for every chart (OnePasswordItem stub CRD) # ci/common/ fixtures applied for every chart (OnePasswordItem stub CRD)
@@ -14,12 +15,48 @@
# ci/excluded-charts.txt charts that cannot run in CI, with reasons # ci/excluded-charts.txt charts that cannot run in CI, with reasons
set -euo pipefail set -euo pipefail
CHART="${1:?usage: $0 <chart-name>}"
NAMESPACE="default" NAMESPACE="default"
RELEASE="ci" RELEASE="ci"
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
CI_DIR="$ROOT/ci" CI_DIR="$ROOT/ci"
# --- The exclusion list, parsed in exactly one place ------------------------
# Two consumers read ci/excluded-charts.txt: this script, and the install-test
# matrix in .github/workflows/validate-charts.yml, which drops the excluded
# charts before the matrix is built (issue #231). Two parsers would be two
# chances to select different charts - and the failure would be silent in the
# worst direction, a chart the workflow believes excluded and the script
# believes testable, or the reverse. So there is one parser, here, and the
# workflow calls it through "--list-excluded" instead of re-implementing it.
#
# The format is one chart per line: first whitespace-separated field the chart
# name, the rest of the line its mandatory justification. Blank lines and lines
# starting with "#" are comments. A line carrying only a name and no reason is
# deliberately NOT an exclusion (the "NF > 1" below): the justification is what
# the README requires, and a bare name would silently drop a chart out of CI
# with nothing to review.
list_excluded() {
[ -f "$CI_DIR/excluded-charts.txt" ] || return 0
{ grep -vE '^[[:space:]]*(#|$)' "$CI_DIR/excluded-charts.txt" || true; } \
| awk 'NF > 1 { name = $1; $1 = ""; sub(/^[[:space:]]+/, ""); print name "\t" $0 }'
}
# The reason a chart is excluded, empty if it is not. Exact string match on the
# name, not a regex, so a chart name is never read as a pattern.
excluded_reason() {
list_excluded | awk -F'\t' -v chart="$1" '$1 == chart { print $2; exit }'
}
# Emits "<chart><TAB><reason>" for every excluded chart. Read by the detect job
# of validate-charts.yml before any chart is picked, which is why this runs
# before the argument is required.
if [ "${1:-}" = "--list-excluded" ]; then
list_excluded
exit 0
fi
CHART="${1:?usage: $0 <chart-name> | --list-excluded}"
# Per-chart override via ci/<chart>/settings.env (INSTALL_TIMEOUT in seconds). # Per-chart override via ci/<chart>/settings.env (INSTALL_TIMEOUT in seconds).
# REQUIRED_ENV is a space-separated list of environment variables the chart's # REQUIRED_ENV is a space-separated list of environment variables the chart's
# fixtures need; see the check below. # fixtures need; see the check below.
@@ -32,13 +69,17 @@ if [ -f "$CI_DIR/$CHART/settings.env" ]; then
fi fi
# --- Exclusion list --------------------------------------------------------- # --- Exclusion list ---------------------------------------------------------
if [ -f "$CI_DIR/excluded-charts.txt" ]; then # Through excluded_reason(), i.e. the same parser --list-excluded uses, so a
REASON="$(grep -E "^$CHART([[:space:]]|$)" "$CI_DIR/excluded-charts.txt" | sed -E "s/^$CHART[[:space:]]*//" || true)" # local run and the workflow matrix can never disagree about which charts are
# excluded. In CI this branch is no longer reached: the detect job drops the
# excluded charts before the matrix is built, so no install-test job exists for
# them (#231). It stays for local runs, where "ci/run-install-test.sh
# satisfactory" should say why it does nothing instead of trying.
REASON="$(excluded_reason "$CHART")"
if [ -n "$REASON" ]; then if [ -n "$REASON" ]; then
echo "SKIP: chart '$CHART' is excluded from the install-test: $REASON" echo "SKIP: chart '$CHART' is excluded from the install-test: $REASON"
exit 0 exit 0
fi fi
fi
if [ ! -d "$ROOT/$CHART" ] || [ ! -f "$ROOT/$CHART/Chart.yaml" ]; then if [ ! -d "$ROOT/$CHART" ] || [ ! -f "$ROOT/$CHART/Chart.yaml" ]; then
echo "ERROR: '$CHART' is not a chart directory" >&2 echo "ERROR: '$CHART' is not a chart directory" >&2
+2 -2
View File
@@ -5,8 +5,8 @@ icon: https://avatars.githubusercontent.com/u/988985
type: application type: application
version: 4.1.0+up1.46.4 version: 4.1.1+up1.46.5
appVersion: "1.46.4" appVersion: "1.46.5"
maintainers: maintainers:
- name: jklein - name: jklein
+2 -2
View File
@@ -5,8 +5,8 @@ icon: https://raw.githubusercontent.com/outline/outline/main/public/images/icon-
type: application type: application
version: 7.1.0+up1.9.2 version: 7.1.2+up1.10.1
appVersion: 1.9.2 appVersion: 1.10.1
maintainers: maintainers:
- name: jklein - name: jklein
+2 -2
View File
@@ -5,8 +5,8 @@ icon: https://raw.githubusercontent.com/lukevella/rallly/main/apps/landing/publi
type: application type: application
version: 8.1.1+up4.13.1 version: 8.1.3+up4.15.0
appVersion: "4.13.1" appVersion: "4.15.0"
maintainers: maintainers:
- name: jklein - name: jklein
+2 -2
View File
@@ -9,8 +9,8 @@ type: application
# (2026.8.22-9fea41204) and "latest" moves daily. appVersion is therefore the # (2026.8.22-9fea41204) and "latest" moves daily. appVersion is therefore the
# exact tag, and the +up suffix normalizes the dash to a dot as the versioning # exact tag, and the +up suffix normalizes the dash to a dot as the versioning
# scheme requires - the same shape rss-bridge and minecraft-server use. # scheme requires - the same shape rss-bridge and minecraft-server use.
version: 1.0.2+up2026.8.29.d226b78bc version: 1.0.4+up2026.9.11.ffe96f8a6
appVersion: "2026.8.29-d226b78bc" appVersion: "2026.9.11-ffe96f8a6"
maintainers: maintainers:
- name: jklein - name: jklein
+2 -2
View File
@@ -4,5 +4,5 @@ description: A Helm chart to deploy urlaubsverwaltung on Kubernetes
icon: https://raw.githubusercontent.com/urlaubsverwaltung/urlaubsverwaltung/main/src/main/resources/static/favicons/apple-touch-icon.png icon: https://raw.githubusercontent.com/urlaubsverwaltung/urlaubsverwaltung/main/src/main/resources/static/favicons/apple-touch-icon.png
type: application type: application
version: 9.0.0+up6.7.0 version: 9.0.2+up6.12.0
appVersion: 6.7.0 appVersion: 6.12.0
+2 -2
View File
@@ -5,8 +5,8 @@ icon: https://raw.githubusercontent.com/l4rm4nd/VoucherVault/main/myapp/static/a
type: application type: application
version: 6.1.0+up1.30.1 version: 6.1.1+up1.30.2
appVersion: "1.30.1" appVersion: "1.30.2"
maintainers: maintainers:
- name: jklein - name: jklein