diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0a8f84c..0a6f59f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -32,6 +32,9 @@ jobs: - name: ๐Ÿงช Test active Admins policy run: bash tests/admin-team-policy.sh + - name: ๐Ÿงช Test declarative coverage + run: bash tests/declarative-coverage.sh + ci-required-checks: name: CI - Required Checks runs-on: ubuntu-latest diff --git a/AGENTS.md b/AGENTS.md index a8be975..fb4bb6c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -76,9 +76,13 @@ structure; implementing PRs use `Fixes #N`. **Validate before every PR** (the sole required check, `CI - Required Checks`, gates on this): ```sh -kubectl kustomize deploy/ > /dev/null # must build clean; this is exactly what ci.yaml runs +kubectl kustomize deploy/ > /dev/null # must build clean +bash tests/admin-team-policy.sh # Admins policy invariants +bash tests/declarative-coverage.sh # every repo declared in every rendered dimension ``` +Those three commands are exactly what `ci.yaml` runs. + `kubectl` (with built-in kustomize) is preinstalled on CI runners. A clean build proves the manifests are well-formed; the Crossplane CRDs themselves are applied/validated **on-cluster** (the `github-config` tenant), not in CI โ€” so a green build is necessary but not sufficient, and any new CR diff --git a/deploy/README.md b/deploy/README.md index eb1c0d4..6eed161 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -93,6 +93,8 @@ before every PR with exactly what CI runs: ```sh kubectl kustomize deploy/ > /dev/null +bash tests/admin-team-policy.sh +bash tests/declarative-coverage.sh ``` See the platform repo's @@ -119,4 +121,9 @@ production render and policy invariants with: ```sh kubectl kustomize deploy/ > /dev/null bash tests/admin-team-policy.sh +bash tests/declarative-coverage.sh ``` + +`declarative-coverage.sh` reads the **rendered** output, so it also catches a +manifest that exists on disk but was never wired into its directory's +`kustomization.yaml` โ€” that file renders to nothing and never reconciles. diff --git a/tests/declarative-coverage.sh b/tests/declarative-coverage.sh new file mode 100755 index 0000000..51a3bfb --- /dev/null +++ b/tests/declarative-coverage.sh @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +# +# Guards the *completeness* of the declarative model in deploy/. +# +# Every repository the org manages should appear in every deploy/ dimension. +# When a repo is created or renamed it is typically added to one or two +# dimensions and quietly missed in the rest, leaving it partly declared: it +# looks managed on inspection while un-modelled settings drift underneath. +# +# Coverage is read from the RENDERED output of `kubectl kustomize deploy/`, +# never from the filenames on disk, because only rendered resources reconcile: +# - a manifest not listed in its directory's kustomization.yaml renders to +# nothing (repository-permissions/ is deliberately in that state today), and +# - a renamed file whose forProvider still names the old repo would look +# declared under a filename-based check while reconciling the old repo. +# Both are exactly the partial-rename / partial-add drift this guard exists to +# catch, so the rendered manifest is the only trustworthy source. + +set -euo pipefail + +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +fail() { + echo "declarative-coverage test: $*" >&2 + exit 1 +} + +for tool in kubectl yq; do + command -v "$tool" >/dev/null || fail "required tool '$tool' not found" +done + +render="$(mktemp)" +work="$(mktemp -d)" +trap 'rm -f "$render"; rm -rf "$work"' EXIT + +kubectl kustomize "$repo_root/deploy" >"$render" || + fail "kubectl kustomize deploy/ failed" +[[ -s "$render" ]] || fail "rendered output is empty" + +# Dimensions, as "