From 751ee6a8187d8cc8a2e26bfc20a2005b2eb641e6 Mon Sep 17 00:00:00 2001 From: Matthias Linhuber Date: Fri, 25 Sep 2026 11:46:38 +0200 Subject: [PATCH] fix(ci): validate the chart version each environment selects, not test1's Both the render tests and the reachability check read chartVersion from test1 and used it for every environment. That inverts the coverage: a chart bump reaches staging and production before the test environments, so the manifests being changed were the ones never rendered against the chart they had asked for, and CI stayed green while production pointed at a version nobody had pulled. Each environment now renders against its own version, and the reachability check covers every distinct version rather than one. Each env.yaml is read on its own because yq separates multiple documents with ---, which would otherwise come back as a version to look up. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/validate.yml | 19 +++++++++++++-- scripts/test-deploy-logic.sh | 43 ++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 941d4a1..af5be63 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -94,11 +94,26 @@ jobs: # that it still runs on a laptop with no helm. In CI an unreachable chart # is a failure, not a skip, or the render coverage disappears without # anything turning red. + # Every version any environment selects, not one environment's. A bump + # reaches staging and production before the test environments, so reading + # test1's version meant the chart a production manifest asked for was the + # one thing never checked. - name: Chart every environment renders against is reachable run: | set -euo pipefail - version="$(yq -r '.spec.platform.chartVersion' environments/test1.eduide.student.k8s.aet.cit.tum.de/env.yaml)" - helm show chart oci://ghcr.io/eduide/charts/eduide --version "$version" >/dev/null + fail=0 + # Read each file on its own: yq separates multiple documents with ---, + # which would otherwise come back as a version to look up. + versions="$(for f in environments/*/env.yaml; do yq -r '.spec.platform.chartVersion' "$f"; done | sort -u)" + for version in $versions; do + if helm show chart oci://ghcr.io/eduide/charts/eduide --version "$version" >/dev/null 2>&1; then + echo "chart $version is published" + else + echo "::error::chart $version is not published; no environment can deploy it" + fail=1 + fi + done + exit $fail - name: Deploy logic tests run: ./scripts/test-deploy-logic.sh diff --git a/scripts/test-deploy-logic.sh b/scripts/test-deploy-logic.sh index da85379..1f7a6cb 100755 --- a/scripts/test-deploy-logic.sh +++ b/scripts/test-deploy-logic.sh @@ -96,10 +96,42 @@ done echo echo "=== storage class follows the cluster ===" CHART="${EDUIDE_CHART:-oci://ghcr.io/eduide/charts/eduide}" -CHART_VERSION=$(yq -r '.spec.platform.chartVersion' "$ROOT/environments/test1.eduide.student.k8s.aet.cit.tum.de/env.yaml") -VER_ARG=() -if [[ "$CHART" == oci://* ]]; then VER_ARG=(--version "$CHART_VERSION"); fi -if helm show chart "$CHART" "${VER_ARG[@]}" >/dev/null 2>&1; then + +# Each environment renders against the chart version it actually selects. This +# read one environment's version and used it for all of them, which inverted the +# coverage: a bump lands on staging and production first, so the environments +# being changed were exactly the ones never rendered against the chart they had +# asked for. +env_ver_arg() { + if [[ "$CHART" == oci://* ]]; then + printf -- '--version %s' "$(yq -r '.spec.platform.chartVersion' "$1")" + fi +} + +# yq over several files separates the documents with ---, so each file is read +# on its own rather than filtering that back out of one stream. +chart_versions() { + local f + for f in "$ROOT"/environments/*/env.yaml; do + yq -r '.spec.platform.chartVersion' "$f" + done | sort -u +} + +chart_reachable() { + local v + if [[ "$CHART" != oci://* ]]; then + helm show chart "$CHART" >/dev/null 2>&1 + return + fi + for v in $(chart_versions); do + if ! helm show chart "$CHART" --version "$v" >/dev/null 2>&1; then + return 1 + fi + done + return 0 +} + +if chart_reachable; then W=$(mktemp -d); trap 'rm -rf "$W"' EXIT { # The same two keys the Cluster defaults step in deploy.yml writes. @@ -111,7 +143,8 @@ if helm show chart "$CHART" "${VER_ARG[@]}" >/dev/null 2>&1; then for f in "$ROOT"/environments/*/env.yaml; do env=$(basename "$(dirname "$f")") ns=$(yq -r '.spec.namespace' "$f") - out=$(helm template eduide "$CHART" "${VER_ARG[@]}" -n "$ns" \ + read -r -a ver_arg <<<"$(env_ver_arg "$f")" + out=$(helm template eduide "$CHART" "${ver_arg[@]}" -n "$ns" \ -f "$W/cd.yaml" -f "$ROOT/environments/_base.yaml" \ -f "$ROOT/environments/$env/values.yaml" -f "$W/sec.yaml" 2>/dev/null) || { bad "$env does not render" ""; continue; }